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:
Post a Comment