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 强制执行类型安全的信息;如果此字段不存在 - 那将是不可能的。
现在为什么 scalac
的Signature
生成Object
(因此调用者的零类型安全)是重复地址.我已经尝试阅读这个问题,但它并不容易阅读 - 我会选择 "I trust you"。
多一点解释:Signature
出现在 java-5
添加泛型时。在那之前,所有 descriptor
引用的 call-sites,将其更改为 Signature
将意味着现有代码将被破坏;因此从未做过。因此 Signature
成为可选的并以不同的方式使用 - 对于 checkcast
。至少这是我强烈倾向于假设的:)
为什么 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 强制执行类型安全的信息;如果此字段不存在 - 那将是不可能的。
现在为什么 scalac
的Signature
生成Object
(因此调用者的零类型安全)是重复地址.我已经尝试阅读这个问题,但它并不容易阅读 - 我会选择 "I trust you"。
多一点解释:Signature
出现在 java-5
添加泛型时。在那之前,所有 descriptor
引用的 call-sites,将其更改为 Signature
将意味着现有代码将被破坏;因此从未做过。因此 Signature
成为可选的并以不同的方式使用 - 对于 checkcast
。至少这是我强烈倾向于假设的:)