今天我遇到了关于泛型类型推断的奇怪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>
是构造函数类型参数,而不是类参数。你可以做得很好
new <String, String, String, String, Integer, Foo, Bar, String>HashMap()
因为HashMap
构造函数不声明任何类型参数。
但是,由于尚未提供类类型参数,因此有效地使用了原始类型。Java 语言规范对原始类型是这样说
的构造函数的类型 (§8.8)、实例方法的类型 (§8.4, §9.4) 或 非静态字段 (§8.3) 未继承自的原始类型 C 的 M 它的超类或超接口是对应的原始类型 在对应于 三.
基本思想是,如果将原始类型与某些泛型方法一起使用,则会擦除该方法的泛型类型。所以使用
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 泛型方法