首页 > 解决方案 > List[Int] 和 List[Integer] 类型擦除的区别

问题描述

为什么List[scala.Int]type erase to List[Object]while IntegerinList[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在第二种情况下被保留了。文档状态

将泛型类型中的所有类型参数替换为其边界,或者Object如果类型参数是无界的。

这可能是由于“界限”条款吗?如果是这样,这个界限在哪里指定?

标签: javascalatype-erasurescala-java-interop

解决方案


不是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,它会通过查看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);
}

如果您查看字节码,您将看到:

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

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

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

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

现在为什么Signatureofscalac生成(因此调用者的Object零类型安全)是重复地址的东西。我已尝试阅读该问题,但这并不容易阅读-我只会选择“我相信你”。


更多解释:Signature出现在java-5添加泛型时。在此之前,所有由 引用的调用站点descriptor,将其更改为Signature将意味着现有代码将中断;因此从未完成。因此Signature成为可选的并以不同的方式使用 - for checkcast。至少这是我强烈倾向于假设的:)


推荐阅读