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

9
为什么List[scala.Int]类型会被擦除成List[Object],而List[java.lang.Integer]中的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(如果类型参数未限定)。

这可能是由于“边界”条款引起的吗?如果是这样,那么这个边界是在哪里指定的呢?


我认为这与此有关 https://docs.scala-lang.org/overviews/core/value-classes.html.html - Pedro Correia Luís
你能把完整的 javap 输出贴在这里吗?请使用 -c -v -p - Eugene
5
可能是为什么Scala中的原始类型如Int被擦除为Object?的重复问题。 - Seth Tisue
1个回答

5

我不是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参数(请继续阅读)来“作弊”,以便向您显示这个无害的小谎言。

现在想一想。让我们把这个方法放在类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

有一个紧急的问题需要解决:如果泛型被擦除了,它是如何通过 checkcast 知道 Integer 的呢?答案是在编译时生成的可选 Signature,或者说在你的情况下:
 ()Lscala/collection/immutable/List<Ljava/lang/Object;>; //fooInt
 ()Lscala/collection/immutable/List<Ljava/lang/Integer;>; // fooInteger

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

现在我们来看看为什么scalacSignature生成Object(因此对调用者没有任何类型安全性)这个问题。我试图阅读这个问题,但它并不容易理解,所以我会选择“相信你”。


更多解释:自从添加泛型以来,Signature出现在java-5中。在此之前,所有调用点都是由descriptor引用的,将其改为Signature将意味着现有代码将无法使用,因此从未完成。因此,Signature变成了可选项,并以不同的方式用于checkcast。至少这就是我非常倾向于假设的事情 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接