问题
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