José D.
José D.

Reputation: 4275

Java generic method. Why is T deduced to be Map?

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

Answers (1)

Philipp Wendler
Philipp Wendler

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

Related Questions