Reputation: 4275
Consider the following code
class MyClass {
public MyClass(Map<String, String> m) {
System.out.println("map");
}
public MyClass(SortedMap<String, String> m) {
System.out.println("sortedmap");
}
}
public class Test {
public <T extends Map<String,String>> Test(T t) {
new MyClass(t);
}
public static void main(String[] args) {
new Test(new TreeMap<String,String>());
}
}
It prints map
. Why is T
deduced to be Map
instead of SortedMap
in public <T extends Map<String, String>> Test(T t)
? Is there a way to change this behaviour in order to use the most concrete constructor for MyClass
?
Upvotes: 6
Views: 97
Reputation: 11433
The resolving which constructor of MyClass
is called is done at compile time. When the compiler compiles the code of the Test
constructor, it does not know what T
actually is, it just knows that it is guaranteed to be a Map<String, String>
, so it cannot do anything else than binding the constructor call to the constructor that takes a Map
.
The knowledge that in your code T
is a TreeMap
is only present within the body of the method main
, not outside. Consider for example what would happen if you added a second caller of the Test
constructor that actually passes a HashMap
.
Java generics work such that the code of a generic method is only compiled once for all possible generic parameter values (and only present once in the byte code), there is not like in other languages a copy of the generic method for each generic type.
In general, it is not possible in Java to let a single method/constructor call in the code actually call different methods/constructors at runtime depending on the type of arguments. This is only possible for method calls depending on the runtime type of the called object (dynamic binding with overwritten methods).
Overloading (what you have here) works only at compile time by looking at the static type of the arguments.
The typical solution for this situation would be to use an instanceof SortedMap
check within the constructor of MyClass
.
Another possible (more elegant) solution is the visitor pattern, but this works only with classes that are prepared for it (so not with Map
instances if you do not wrap them within a class of your own).
Upvotes: 4