Home » » understanding Java Generics through Erasure

understanding Java Generics through Erasure


Now
that I’ve been playing with generics for nearly a year, I think I
better understand how to convey what’s really going on with them in one
simple example.

Erasure for Compilation


Consider the following very simple class which merely holds a reference to a character sequence generically:


    class Foo<E extends CharSequence> {
E mE;
Foo(E e) {
mE = e;
}
E get() {
return mE;
}
}

The first key to understanding this class is to understand how it looks with all the generics "erased":


    class Foo {
CharSequence mE;
Foo(CharSequence e) {
mE = e;
}
CharSequence get() {
return mE;
}
}

The erased version is the one that is compiled. Thus a client can
use the class with no generics whatsoever according to the erased
version.


Note that the erasure left the interface being extended by the generic E behind. So clients can assign the results of a get to a CharSequence variable. They cannot construct an instance by calling something like new Foo(new Integer(5)), because it doesn’t match the erasure.


Casting for Clients


Now what happens with clients of the class that use the generic. For instance, I might do this:


    Foo<String> foo = new Foo<String>("bar");
String value = foo.get();

If the target of compilation is the erased class, how is this simple
client able to assign the value of the getter to a string? The answer
is that the compiler inserts implicit casts, so that the code above is
equivalent to one with appropriate casts inserted:


    Foo<String> foo = new Foo<String>("bar");
String value = (String) foo.get();

The guarantee is that the casts so-inserted will not cause run time cast class exceptions, because the declaration of foo as Foo<String> requires a compile-time check that all arguments match the declaration. Thus we can do this:


    new Foo(new StringBuilder());

we cannot do this:


    new Foo<String>(new StringBuilder());

It will fail the compile-time test.


Why no Instances?


The erased form of the class indicates why it’s impossible to create
generic arrays. For instance, I couldn’t declare a method in Foo like this:


    E[] toArray() {
return new E[] { mE };
}

The problem is with the erasure:


    CharSequence[] toArray() {
return new CharSequence[] { mE };
}

and the corresponding client code:


    Foo<String> foo = new Foo<String>("bar");
String[] s = foo.toArray();

Specifically, the implicit cast causes an error:


    String[] s = (String[]) foo.toArray();

This is because Java doesn’t let you cast a CharSequence[] to a String[]

0 Comments:

Popular Posts