Issue
I have an implementation of the following interface:
Object map(Object object);
Entity map(Dto object);
Dto map(Entity object);
When I call map(new Dto())
I would expect that the method Entity map(Dto object)
gets called. Altought the method Object map(Object object)
gets invoked.
What, in the jungle of typeerasure and overloading, am I missing?
Solution
What you're missing is how java works.
A method's "identity", the thing that is written in the class file, is not just the method name. It's the method name, and the 'type' that it comes from, and the erased types (but not names) of the parameters, and the return type.
So, your 3 map methods there? They are different methods. They are nothing alike, they are not overloads of each other, they are as different as void foo() {}
and void bar() {}
are.
Java does have dynamic dispatch and overriding, but only if the signatures line up.
javac
decides which method id you are invoking (and, remember, the 'id' of a method is the name and the param types and the return type). the runtime (java
) then picks the most specific override of that method id to run.
Here's some java code to drive the point home:
class Parent {
void foo(Object o) {
System.out.println("Parent.foo(Object)");
}
void foo(String o) {
System.out.println("Parent.foo(String)");
}
}
class Child extends Parent {
void foo(String o) {
System.out.println("Child.foo(String)");
}
}
class Main {
public static void main(String[] args) {
Parent a = new Child();
a.foo("hello"); // prints 'Child.foo(String)'
Object b = "hello";
a.foo(b); // prints 'Parent.foo(Object)'
a.foo((String) b); // prints 'Child.foo(String)'
}
}
The upshot is that you cannot use 'tightening' the types in an overloaded method for dynamic dispatch. i.e. you can't go: "Oh, I want to override the map
method, but only if the parameter is this subtype. If it's not of this subtype I just want it to go to parent". If you want that behaviour, you have to override the method:
class Parent {
Object map(Object object) {}
}
class Child {
Object map(Object object) {
if (object instanceof Dto dto) return childMap(dto);
return super.map(object);
}
Object childMap(Dto dto) {
// your code specifically for Dto goes here
}
}
I strongly advise naming that custom child-based method differently to avoid confusion. The @Override
annotation can be used to guard against mistakes. If you think a method is an override, always annotate it as such (that method doesn't make a method an override, it merely causes the compiler to error out if it is not. It's compiler-checked documentation).
Answered By - rzwitserloot
Answer Checked By - Clifford M. (JavaFixing Volunteer)