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

2022-09-03 14:34:46

让我们考虑一个类:

public class Foo<T> {
    public List<String> list = new ArrayList<>();
}

我作为参数传递给方法。


我有一个问题,理解为什么这里的类型没有得到解决:String

public void test(Foo t) {
    t.list.get(0).contains("test");
}

并且被视为在这里一切都很好:t.listList<Object>

public void test(Foo<?> t) {
    t.list.get(0).contains("test");
}

并且是 .t.listList<String>


有关类型擦除的其他已链接问题,从不同的角度处理问题。在不知道我自己的问题的答案的情况下,我没有看到连接,这就是为什么我不认为这个问题是重复的。


答案 1

当使用 时,是原始类型,因此其非静态、非继承的成员(如上面的代码中所示)也是原始类型。以下是Java语言规范的相关部分(见上面的链接):Foo ttlist

为了便于与非泛型遗留代码进行接口,可以使用参数化类型 (§4.5) 的擦除 (§4.6) 或元素类型为参数化类型的数组类型 (§10.1) 的擦除作为类型。此类类型称为原始类型。

更准确地说,原始类型被定义为:

  • . . .

  • 原始类型 R 的非静态成员类型,它不是从 R 的超类或超接口继承的。

和/或

原始类型 C 的构造函数 (§8.8)、实例方法 (§8.4、 §9.4) 或非静态字段 (§8.3) 的类型(不是从其超类或超接口继承的)是原始类型,它与在对应于 C 的泛型声明中其类型的擦除相对应。

只需声明 as,它就会被解释为预期的 a用于测试)。liststaticListString

另一方面,声明不是原始类型,因此也不被视为原始类型。Foo<?>list

该页面后面的建议

原始类型的使用仅允许作为对遗留代码兼容性的让步。强烈建议不要在将泛型引入 Java 编程语言后编写的代码中使用原始类型。Java编程语言的未来版本可能会禁止使用原始类型。

注意:类型擦除和生成的字节码在这两种情况下是相同的...


答案 2

这就是类型擦除的工作原理。

如果类型参数未绑定,则将泛型类型中的所有类型参数替换为其边界或 Object。因此,生成的字节码仅包含普通类、接口和方法。

由于您的方法接受原始类型,因此编译器通过擦除类型并将其替换为 来应用类型擦除。这就是无法识别的原因,因为没有该方法调用。StringObjectcontainsObject

通过提供,您可以提供一个有界类型,该类型将用于类型擦除。在那里,编译器将识别因为它。<?>containsString


推荐