1

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.

3
  • 5
    This can be solved (at some cost) via the visitor pattern. Commented Apr 17, 2016 at 10:51
  • A model -> execute(A), what is a problem? Commented Apr 17, 2016 at 10:54
  • @OliverCharlesworth hadn't heard of that one yet; it seems pretty interesting indeed.. Commented Apr 17, 2016 at 10:56

4 Answers 4

5

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.

Sign up to request clarification or add additional context in comments.

2 Comments

@OliverCharlesworth Agreed, and the Visitor pattern is the answer, as you suggested.
Accepted the other answer because it's more detailled, however yours is correct too.
3

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 to execute(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.

Comments

2

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

Comments

-2

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);
    }

2 Comments

Never use instanceof
I would not say never but I agree that there's a better way to do it :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.