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?
- Erasure is a type of alteration in document. It can be classified as chemical erasure and physical erasure.
Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.
Type erasure refers to the runtime encoding of parameterized classes in Scala. It is simply performed by Scala compiler in which it removes all the generic type information after compilation. In Scala, generics are erased at runtime, which means that the runtime type of List[Int] and List[Boolean] is actually the same.
C# (actually . NET generics in general) does not use type erasure. Java uses type erasure. C# does not.
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 :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With