today I have ran into strange javac
behavior regarding generic type inference. Here's example class to ilustrate this strange behavior:
import java.util.Map;
import java.util.Collections;
import java.util.HashMap;
public class Test {
protected <T> T strange(T t, Map<String, String> map) {
return t;
}
protected void ok(Map<String, String> map) {}
protected <T> T test(T t) {
T res = strange(t , new HashMap<String, String>());
//Doesn't work
//res = strange(t, new <String, String>HashMap());
ok(new <String, String>HashMap());
res = strange(t, Collections.<String, String>emptyMap());
//Doesn't work
//res = strange(t, Collections.EMPTY_MAP);
res = strange(t, (Map<String, String>) Collections.EMPTY_MAP);
ok(Collections.EMPTY_MAP);
return res;
}
}
Notice //Doesn't work
comments. If you uncomment this code you will get strange compiler error:
Test.java:18: error: incompatible types
res = strange(t, Collections.EMPTY_MAP);
^
required: T
found: Object
where T is a type-variable:
T extends Object declared in method <T>test(T)
Strange thing is that error complains about strange
method return type is Object and not T, but when second unsafe parameter is cast to correct type then T is inferred correctly.
Can someone explain if this is correct behavior? Because it looks strange to me. Can it be a compiler bug?
Why this line works
T res = strange(t , new HashMap<String, String>());
and this one is doesn't?
T res = strange(t, new <String, String>HashMap());
I have tested it with java 7 and 6.
The root cause is the use of Raw Types.
When you do this
The type arguments
<String, String>
are constructor type arguments, not class arguments. You can very well dosince the
HashMap
constructor does not declare any type parameters.However, because you haven't provided the class type arguments, you're effectively using a raw type. The Java Language Specification says this about raw types
The basic idea is that if you use a raw type with some generic method, the generic types of the method are erased. So using
makes your method look like
to the compiler.
and you are trying to do
You can't assign
Object
to a reference of typeT
because you don't know whatT
is. The compiler therefore throws an exception.Related