Home > Enterprise >  Failure to choose a right overload for method called from a generic method
Failure to choose a right overload for method called from a generic method

Time:01-28

I have a following code:

class A{}
class B{}
class StuffDoer {
  public void doStuff(A a) { System.out.println("A"); }
  public void doStuff(B b) { System.out.println("B"); }
  public void doStuff(Object o) { System.out.println("Object"); }
}

class Test {
  private final StuffDoer stuffDoer;

  public <T> void foo(T t) {
    stuffDoer.doStuff(t)
  }
}

And following execution

Test test = new Test();
A a = new A();
B b = new B();
test.foo(a);
test.foo(b);

prints "Object" twice, instead of expected "A" and "B" afterwards.

It doesn't work if i explicit pass a Class object either

class Test {
  private final StuffDoer stuffDoer;

  public <T> void foo(T t) { //doesnt work
    stuffDoer.doStuff(t.getClass().cast(t))
  }

  public <T> void foo(T t, Class<T> tClass) { //doesnt work either
    stuffDoer.doStuff(tClass.cast(t))
  }

}

It only works if i explicity cast them to proper object in the foo method like this

class Test {
  private final StuffDoer stuffDoer;

  public <T> void foo(T t) {
    if ( t instanceof A ) 
      stuffDoer.doStuff((A) t) // Prints "A"
    else if ( t instance of B ) 
      stuffDoer.doStuff((B) t) // Prints "B"
    else
      stuffDoer.doStuff(t) // Prints "Object"
  }
}

Why is that? How can i achive right method overloading from a generic type? Is it even possible in Java?

CodePudding user response:

In Java only the method receiver is picked dynamically at runtime whereas the signature of a method is determined at compile time using the provided argument type(s), so that doStuff(Object o) method is being chosen as the only possible (commenting it out would result in a compile-time error).

There aren't many options around to "emulate" multiple dispatch in the way you want:

  • A chain of instanceof as you already mentioned

  • A Visitor/Strategy pattern

  • Dynamic method lookup via Reflection or MethodHandle

  • Pattern matching (since Java 17):

    public <T> void foo(T t) {
        switch (t) {
            case A a  -> stuffDoer.doStuff(a);
            case B b  -> stuffDoer.doStuff(b);
            case null -> System.out.println("OOPS :(");
            default   -> stuffDoer.doStuff(t);
        }
    }
    
  •  Tags:  
  • Related