List [Int]和List [Integer]的类型擦除差异

时间:2019-07-18 17:38:59

标签: java scala type-erasure scala-java-interop

为什么在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

  

使用边界将通用类型中的所有类型参数替换为边界,如果类型参数不受限制,则用Object替换。

这可能是由于“ bounds”子句引起的吗?如果是这样,该界限在哪里指定?

1 个答案:

答案 0 :(得分: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

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

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

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

现在,{em}的Signature的{​​{1}}会生成scalac(因此,呼叫者的零类型安全)是重复地址。我已尝试阅读该问题,但阅读起来并不容易-我会选择“我信任您”。


更多解释:Object出现在Signature中,当时添加了泛型。在此之前,java-5引用的所有呼叫站点将其更改为descriptor意味着现有代码将中断;因此从来没有做过。因此Signature成为可选的,并以不同的方式使用-用于Signature。至少这是我强烈想要的:)