List[Int] 和 List[Integer] 类型擦除的区别

Difference in type erasure of List[Int] and List[Integer]

为什么 List[scala.Int] 键入擦除到 List[Object]Integer 似乎是 List[java.lang.Integer] 被保存?例如,javap 代表

object Foo {
  def fooInt: List[scala.Int] = ???
  def fooInteger: List[java.lang.Integer] = ???
}

产出

public scala.collection.immutable.List<java.lang.Object> fooInt();
public scala.collection.immutable.List<java.lang.Integer> fooInteger();

我们看到 Integer 在第二种情况下被保留。文档 state

Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded.

这可能是由于 "bounds" 子句造成的?如果是这样,这个界限在哪里指定?

不是 scala 开发人员,对此持保留态度。擦除相同:

public static scala.collection.immutable.List<java.lang.Object> fooInt();
descriptor: ()Lscala/collection/immutable/List;

public static scala.collection.immutable.List<java.lang.Integer> fooInt();
descriptor: ()Lscala/collection/immutable/List;

查看descriptor参数;这就是 调用站点 在字节代码级别引用的内容。

当你简单地做 javap 时,它 "cheats" 通过查看 Signature 参数(进一步阅读)一点点,以便它向你展示这个无害的小谎言。

现在想想。让我们把这个方法放在 class A:

static List<Integer> test() {
    return null; // or whatever that is not the point
} 

我们编译它,将 .class 文件分享给其他人。其他人以这种形式使用它:(实际上没有 A 的源代码)。

public void testMe() {
    Integer x = A.test().get(0);
}

如果您查看 byte-code,您会看到:

    5: invokeinterface #3,  2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
    10: checkcast     #4      // class java/lang/Integer

有一个直接的问题必须出现:它如何知道 Integer(通过那个 checkcast)如果泛型被擦除?答案是编译 A 时生成的可选 Signature,或者在您的情况下:

 ()Lscala/collection/immutable/List<Ljava/lang/Object;>; //fooInt
 ()Lscala/collection/immutable/List<Ljava/lang/Integer;>; // fooInteger

Signature 信息是编译器用来通过运行时检查在 callsites 强制执行类型安全的信息;如果此字段不存在 - 那将是不可能的。

现在为什么 scalacSignature 生成Object(因此调用者的零类型安全)是重复地址.我已经尝试阅读这个问题,但它并不容易阅读 - 我会选择 "I trust you"。


多一点解释:Signature 出现在 java-5 添加泛型时。在那之前,所有 descriptor 引用的 call-sites,将其更改为 Signature 将意味着现有代码将被破坏;因此从未做过。因此 Signature 成为可选的并以不同的方式使用 - 对于 checkcast。至少这是我强烈倾向于假设的:)