今天我遇到了关于泛型类型推断的奇怪 javac
行为。这是示例类来说明这种奇怪的行为:
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;
}
}
注意//Doesn't work
注释。如果取消注释此代码,您将收到奇怪的编译器错误:
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
方法返回类型是 Object 而不是 T,但是当第二个不安全参数转换为正确类型时,T 会被正确推断。
有人可以解释这是否是正确的行为吗?因为它对我来说看起来很奇怪。这可能是编译器错误吗?
为什么这条线有效
T res = strange(t , new HashMap<String, String>());
这个不是吗?
T res = strange(t, new <String, String>HashMap());
我已经用 java 7 和 6 对其进行了测试。
最佳答案
根本原因是原始类型的使用。
当你这样做时
new <String, String>HashMap()
类型参数 <String, String>
是constructor type arguments ,而不是类参数。你可以很好地做到
new <String, String, String, String, Integer, Foo, Bar, String>HashMap()
自 HashMap
构造函数不声明任何类型参数。
但是,由于您没有提供类类型参数,因此您实际上正在使用原始类型。 Java Language Specification says this about raw types
The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.
基本思想是,如果您将原始类型与某些泛型方法一起使用,则该方法的泛型类型为 erased 。所以使用
res = strange(t, new <String, String>HashMap());
使你的方法看起来像
protected Object strange(Object t, Map map) {
return t;
}
到编译器。
你正在尝试做
/* T */ res = strange(t, new <String, String>HashMap());
您无法分配 Object
到 T
类型的引用因为你不知道什么T
是。因此编译器会抛出异常。
相关
关于如果嵌套方法参数不是类型安全的,Java 泛型会推断嵌套方法调用的对象而不是 T,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20669543/