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

南笙酒味 提交于 2019-12-08 16:23:41

问题


Why does List[scala.Int] type erase to List[Object] whilst Integer in List[java.lang.Integer] seems to be preserved? For example, javap for

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

outputs

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

where we see Integer was preserved in second case. The docs state

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

Is this perhaps due to the "bounds" clause? If so, where is this bound specified?


回答1:


I am not a scala developer, take this with a grain of salt. The erasure is the same:

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;

look at the descriptor parameter; that is what gets referenced at call sites at the byte code level.

When you simply do javap, it "cheats" a little bit by looking at the Signature parameter (read further) so that it shows you this little inoffensive lie.

Now think about it. Let's take this method and place it in class A:

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

we compile it, share the .class file to someone else. That someone else uses it in this form: (without actually having source code for A).

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

if you look at the byte-code, you will see:

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

There is an immediate question that has to arise: how does it know about Integer (via that checkcast) if generics are erased? The answer is the optional Signature that is generated when A is compiled, or in your cases:

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

This Signature information is what is used by the compiler to enforce type safety at callsites, via runtime checks; if this field would not be present - that would have been impossible.

Now to why the Signature of scalac generates Object (thus zero type safety for callers) is something that the duplicate addresses. I've tried to read the issue and it's not an easy read- I'll just go with "I trust you".


A little more explanations: Signature appeared in java-5 when generics where added. Until then, all call-sites where referenced by descriptor, changing that to Signature instead would mean that existing code would break; thus never done. Thus Signature became optional and used in a different way - for checkcast. At least this is what I am strongly inclined to assume :)



来源:https://stackoverflow.com/questions/57099984/difference-in-type-erasure-of-listint-and-listinteger

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!