没有类型的泛型作为方法参数-无法解析其字段类型

时间:2018-09-19 10:27:50

标签: java generics

让我们考虑一堂课

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.listList<String>


与类型擦除有关的其他问题已从另一个角度解决了这个问题。不知道我自己问题的答案,我看不到连接,这就是为什么我不认为这个问题重复的原因。

2 个答案:

答案 0 :(得分:9)

使用Foo t时,tRaw 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,因此编译器将在其中识别出它们。