Reputation: 1961
The following context is needed: The purpose of this way of coding is to avoid if
-else
statements and instanceof
; which is always a bad idea.
I have 3 classes with the following signatures:
abstract class A {}
class B extends A {}
class C extends A {}
Then I have another class with the following structure:
class MyClass {
private final A model;
public MyClass(A m) {
this.model = m;
}
public void doSomething() {
System.out.println(this.model instanceof C); //TRUE!!
execute(this.model);
}
private void execute(A m) {
System.out.println("noo");
}
private void execute(C m) {
System.out.println("yay");
}
}
And finally the contents of my main:
public static void main(String... args) {
C mod = new C();
MyClass myClass = new MyClass(mod);
myClass.doSomething();
}
Now the problem; the execute(C) method never gets executed, it's always the execute(A) method. How can I solve this? I cannot change the signature of the execute(A) method to execute(B) since that would give an error saying java "cannot resolve method execute(A)" at MyClass#doSomething.
Upvotes: 1
Views: 113
Reputation: 1
You are sending object of type C as an object of type A in constructor( you've done upcasting) and assigning it to a reference to type A(which will result in calling only execute(A) method).You could check if the object is a instance of C and depending on the outcome, call the desired method. You could do it like this
public void doSomething(){
System.out.println(model instanceof C);
if (model instanceof C) execute((C)model);
else
execute(model);
}
Upvotes: -2
Reputation: 726479
Your code illustrates the difference between a static and a dynamic type of an object. Static type is what's known to the compiler; dynamic type is what's actually there at runtime.
The static type of your model
field is A
:
private final A model;
That is, the compiler knows that A
itself or some of its implementations is going to be assigned to model
. The compiler does not know anything else, so when it comes to choosing between execute(A m)
and execute(C m)
its only choice is execute(A m)
. The method is resolved on the static type of the object.
instanceof
, on the other hand, understands the dynamic type. It can tell that the model
is set to C
, hence reporting the true
in your printout.
You can solve it by adding a method to A
and overriding it in B
and C
to route to the proper execute
:
abstract class A {
public abstract void callExecute(MyClass back);
}
class B extends A {
public void callExecute(MyClass back) {
back.execute(this);
}
}
class C extends A {
public void callExecute(MyClass back) {
back.execute(this);
}
}
class MyClass {
private final A model;
public MyClass(A m) {
this.model = m;
}
public void doSomething() {
System.out.println(this.model instanceof C); //TRUE!!
model.callExecute(this.model);
}
public void execute(B m) {
System.out.println("noo");
}
public void execute(C m) {
System.out.println("yay");
}
}
Note that both implementations call
back.execute(this);
However, the implementation inside B
has this
of type B
, and the implementation inside C
has this
of type C
, so the calls are routed to different overloads of the execute
method of MyClass
.
I cannot change the signature of the
execute(A)
method toexecute(B)
Also note that now you can (and should) do that, too, because callbacks are performed to the correct overload based on type of this
.
Upvotes: 3
Reputation: 310850
Method overloads are resolved at compile time. At compile time, the type of m
is A
, so execute(A m)
gets executed.
In addition, private methods are not overridable.
The solution is to use the Visitor pattern as suggested by @OliverCharlesworth.
Upvotes: 5
Reputation: 8641
Method overloading is a compile time polymorphism. Thus, for calling method execute(C)
you need to define your model as class C
.
It's better to define method execute()
in class A
and override it in subclasses.
abstract class A {
abstract void execute();
}
class B extends A {
public void execute(){};
}
class C extends A {
public void execute(){};
}
And then:
class MyClass {
private final A model;
public void doSomething() {
model.execute();
}
This much better way to use polymorphism to avoid if-else statements and instanceof checking
Upvotes: 2