Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 559
José D. Avatar asked Jan 17 '15 14:01

José D.


People also ask

Why is remove method not generic in map as well as in collection?

remove() (in Map as well as in Collection ) is not generic because you should be able to pass in any type of object to remove() . The object removed does not have to be the same type as the object that you pass in to remove() ; it only requires that they be equal.

How do you initialize a generic map in Java?

Suppose if the key is of type String and the corresponding value is of type Integer, then we can initialize it as, Map< String , Integer > map = new HashMap< String ,Integer >(); The map can now only accept String instances as key and Integer instances as values.

What are generic maps?

Description: A generic map gives the value to a generic. Usually given in an instance but can also appear in a configuration. The values can be given via positional association or via named association. Use of named association is advised to improve readability and reduce the risk of making errors.

What is the motivation for using generic in Java?

In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods. Much like the more familiar formal parameters used in method declarations, type parameters provide a way for you to re-use the same code with different inputs.


1 Answers

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).

like image 174
Philipp Wendler Avatar answered Oct 01 '22 03:10

Philipp Wendler