4

我怀疑阅读 Java教程中写的这篇文章:

在介绍中,我们看到了泛型类型声明 List 的调用,例如 List。在调用(通常称为参数化类型)中,所有出现的形式类型参数(在本例中为 E)都被实际类型参数(在本例中为 Integer)替换。

但是如果没有限制,形式类型参数不会被 Object 替换吗?为什么说E被Integer代替?

另外,在这里,在Java教程中说:

要从代码中引用泛型 Box 类,您必须执行泛型类型调用,它将 T 替换为某个具体值,例如 Integer:

但是,再次感谢擦除,box 类中的编译时间 T 被 Object 而不是 Integer 取代。整数类型仅用于强制转换操作。

其实还是在同一个教程里说的:

在类型擦除过程中,Java 编译器擦除所有类型参数,如果类型参数是有界的,则将每个类型参数替换为其第一个边界,如果类型参数是无界的,则将其替换为 Object。

我真的很困惑。哪个是真相?被或T取代?IntegerObject

4

2 回答 2

2

你说的是不同的东西。

教程中的引文谈到了类型实例化。这与类型擦除无关,这是恕我直言错误命名的概念,只是意味着泛型类型在运行时不再可用。

但是在编译时它们是,并且实例化发生在编译时。

要回答您的问题,“在编译时”是一个广泛的问题。以下情况在编译时发生:

  1. 读取源文件
  2. 词法分析
  3. 解析
  4. ...
  5. 类型检查
  6. ...
  7. 代码生成

请注意,该列表绝不是完整的。但是,如您所见,在类型检查期间,编译器知道您的类型实例化并可以检查它们。

后来,它发出字节码,由于字节码无法表示泛型,类型被“擦除”,这意味着在这里和那里插入了一个强制转换。

因此,您认为“编译时间”在某种程度上是所有事情同时发生的瞬间的假设是不正确的。


进一步编辑:

我认为您对所有这些(即“替换”一词)的理解过于从字面上理解了。当然,编译器有一些数据结构,其中保存了程序中所有项目的类型、名称和范围。

看,原则上很简单,如果我们有:

static <X> List<X> meth(X[] arr) { .... }

后来,你这样做:

Integer arr = new Integer[100];
List<Integer> list = meth(arr);
Integer foo = list.get(1);

然后你正在实例化 meth 方法的类型:

static List<Integer> meth(Integer[] arr) { .... }

泛型的意义在于它meth适用于任何类型。这正是编译器检查的内容。它会知道,对于所有 X,如果你传递一个 X 的数组,你会得到一个 X 的列表,因此,由于你传递了 Integer[],结果必须是List<Integer>并且list赋值是正确的。此外,编译器知道 ** 对于所有 X **,如果您从 a 中获取一个元素List<X>,它将是一个 X。

因此,编译器会记录并检查 foo 是否为Integer. 稍后,在代码生成时,它将在那里插入一个转换为 Integer,因为由于类型擦除,List.get 的返回值是 Object。

另请注意,“替换”并不意味着编译器会以某种方式更改您的代码。它只是从泛型类型签名创建(可能是临时的)一个非泛型签名(通过替换 - 如果你更喜欢这个 - 所有类型参数及其实际类型),并使用它来检查类型。

就像在数学中一样,如果我说:请将 a 替换为 42 并检查等式是否为真:

一个 + 1 = 43

那么问这个替换发生的“确切位置”是没有意义的。很可能在你的大脑中。

于 2013-11-07T15:32:27.537 回答
1
the formal type parameter is not replaced by Object?

Object在运行时表示的泛型类型。但是您可以<YourType>通过反射获得有关信息。擦除涉及与旧类的兼容性。这是个坏主意。关于它的文章

于 2013-11-07T13:46:36.390 回答