【问题标题】:A question about the **Type Erasure mechanism** when read the Core Java 11th ed阅读 Core Java 第 11 版时关于 **Type Erasure 机制** 的问题
【发布时间】:2020-06-20 21:54:30
【问题描述】:

这是作者在书中提供的关于泛型编程的代码示例:

public class Pair<T>
{
  private T first;
  private T second;
  public Pair() { first = null; second = null; }
  public Pair(T first, T second) { this.first = first; this.second = second; }
  public T getFirst() { return first; }
  public T getSecond() { return second; }
  public void setFirst(T newValue) { first = newValue; }public void setSecond(T newValue) { 
  second = newValue; }
}

接下来作者介绍类型擦除机制

无论何时定义泛型类型,都会自动提供相应的原始类型。这 原始类型的名称只是泛型类型的名称,删除了类型参数。 类型变量被擦除并替换为它们的边界类型(或者例如,原始的 为无边界变量的 Object 类型)。

您的程序可能包含不同类型的擦除将它们全部变成原始 Pair ,例如 Pair&lt;String&gt;Pair&lt;LocalDate&gt; ,但擦除会将它们全部转换为原始 Pair 类型。

所以对于class Pair&lt;T&gt;的情况,擦除后变成下面的raw Pair类型

public class Pair
{
  private Object first;private Object second;
  public Pair(Object first, Object second)
  {
  this.first = first;
  this.second = second;
  }
  public Object getFirst() { return first; }
  public Object getSecond() { return second; }
  public void setFirst(Object newValue) { first = newValue; }
  public void setSecond(Object newValue) { second = newValue; }
}

所以如果我们运行代码,打印 TURE

Pair<Integer> p = new Pair<Integer>(1, 2);
Pair<String> s = new Pair<String>("a", "b");
System.out.println(p.getClass()==s.getClass());

问题来了,当我创建一个类型错误的Pair&lt;T&gt; 实例时,例如:

 Pair<String> s = new Pair<String>("a", 1);

编译器可以找出问题并打印此消息:

pair1/PairTest1.java:12:错误:不兼容的类型:int 不能 转换为字符串 对 s = new Pair("a", 1);

因为 &lt;T&gt; 被擦除为 Object 并且构造函数变为:

public Pair(Object first, Object second)
  {
  this.first = first;
  this.second = second;
  }

而参数类型变成了Object,所以:

编译器如何判断类型错误?类型擦除何时发生?

【问题讨论】:

  • 类型擦除什么时候发生?编译后。
  • 不,它发生在编译期间,但编译器并没有忘记原始声明。 @JohannesKuhn

标签: java types casting type-conversion jvm


【解决方案1】:

类型擦除发生在编译器生成字节码时,但编译器在验证源代码时知道泛型类型。那时还没有删除任何内容。

然而,编译器知道即使在验证期间也会发生擦除。例如。如果你重载这样的方法:

void doStuff(List<Integer> intList) {}
void doStuff(List<String> stringList) {}

编译器知道擦除后两者都是void doStuff(List xxx) {},并且您不能有两个具有相同签名的方法1,因此验证步骤会为此生成错误消息,即使类型擦除尚未发生。

1) 参数名称不是签名的一部分

【讨论】:

  • 另外值得注意的是,编译器默认会保存一些泛型类型信息到生成的类文件中,但仅限于类和成员,因此即使在运行时也可以找到泛型类型的字段/方法。但是除了反射之外,运行时并没有真正使用此信息,并且它还允许编译器稍后知道这些泛型类型,即使您依赖于一些已经编译的代码。因为我认为这是 OP 缺少的信息的一部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多