让我们考虑一堂课
public class Foo<T> {
public List<String> list = new ArrayList<>();
}
我将其作为参数传递给方法。
我在理解为何无法解析类型String
时遇到问题:
public void test(Foo t) {
t.list.get(0).contains("test");
}
,而t.list
被视为List<Object>
,而此处的一切工作正常:
public void test(Foo<?> t) {
t.list.get(0).contains("test");
}
,t.list
是List<String>
。
与类型擦除有关的其他问题已从另一个角度解决了这个问题。不知道我自己问题的答案,我看不到连接,这就是为什么我不认为这个问题重复的原因。
答案 0 :(得分:9)
使用Foo t
时,t
是Raw Type,因此它的非静态,非继承成员(如上述代码中的list
)也是原始类型。这里是Java语言规范的相关部分(请参见上面的链接):
为便于与非通用遗留代码接口,可以将参数化类型(第4.5节)的擦除(第4.6节)或元素类型为数组类型的数组类型(第10.1节)擦除作为类型。参数化类型。这样的类型称为原始类型。
更准确地说,原始类型定义为以下类型之一:
。 。
原始类型R的非静态成员类型,它不是从R的超类或超接口继承的。
和/或
原始类型C的构造函数(§8.8),实例方法(§8.4,§9.4)或非静态字段(§8.3)的类型,不是从其超类或超接口继承的是原始类型,它对应于与C对应的通用声明中其类型的擦除。
只需将list
声明为static
,它将按预期解释为List
中的String
(用于测试 )。
另一方面,声明Foo<?>
不是原始类型,因此list
也未被视为原始类型。
该页面后面的advice:
仅允许出于对遗留代码兼容性的考虑而使用原始类型。强烈建议不要在将泛型引入Java编程语言后在编写的代码中使用原始类型。将来的Java编程语言版本可能会禁止使用原始类型。
注意:这两种情况下的类型擦除和生成的字节码是相同的...
答案 1 :(得分:6)
Type Erasure就是这样工作的。
将通用类型中的所有类型参数及其边界或对象替换为无边界的对象。因此,产生的字节码仅包含普通的类,接口和方法。
由于您的方法接受原始类型,因此编译器通过擦除类型String
并将其替换为Object
来应用类型擦除。这就是为什么contains
无法识别的原因,因为Object
没有该方法调用。
通过提供<?>
,您可以提供有界的类型,该类型将用于擦除类型。由于contains
拥有String
,因此编译器将在其中识别出它们。