I think there must be something subtle going on here that I don\'t know about. Consider the following:
public class Foo {
private T[] a = (T[]) ne
Foo<Double> f = new Foo<Double>();
When you use this version of the generic class Foo, then for the member variable a
, the compiler is essentially taking this line:
private T[] a = (T[]) new Object[5];
and replacing T
with Double
to get this:
private Double[] a = (Double[]) new Object[5];
You cannot cast from Object to Double, hence the ClassCastException.
Update and Clarification: Actually, after running some test code, the ClassCastException is more subtle than this. For example, this main method will work fine without any exception:
public static void main(String[] args) {
Foo<Double> f = new Foo<Double>();
System.out.println(f.getA());
}
The problem occurs when you attempt to assign f.getA()
to a reference of type Double[]
:
public static void main(String[] args) {
Foo<Double> f = new Foo<Double>();
Double[] a2 = f.getA(); // throws ClassCastException
System.out.println(a2);
}
This is because the type-information about the member variable a
is erased at runtime. Generics only provide type-safety at compile-time (I was somehow ignoring this in my initial post). So the problem is not
private T[] a = (T[]) new Object[5];
because at run-time this code is really
private Object[] a = new Object[5];
The problem occurs when the result of method getA()
, which at runtime actually returns an Object[]
, is assigned to a reference of type Double[]
- this statement throws the ClassCastException because Object cannot be cast to Double.
Update 2: to answer your final question "why do arrays break this?" The answer is because the language specification does not support generic array creation. See this forum post for more - in order to be backwards compatible, nothing is known about the type of T at runtime.
@matt b: Thanks for the answer! Very helpful.
I have found a workaround for those interested: give the getA method an initialized array to populate. That way the type info is available.
public class Foo<T> {
private T[] a = (T[]) new Object[5];
public Foo() {
// Add some elements to a
}
public T[] getA(T[] holdA) {
// Check whether holdA is null and handle it...then:
holdA = (T[]) Array.newInstance(holdA.getClass().getComponentType(), a.length);
System.arraycopy(a, 0, holdA, 0, a.length);
return holdA;
}
}
Then for your main method:
public static void main(String[] args) {
Foo<Double> f = new Foo<Double>();
Double[] a2 = new Double[1];
a2 = f.getA(a2);
}
There may be some small errors in @mattb's explanation.
The error is not
java.lang.Object cannot be cast to java.lang.Double.
It is:
[Ljava.lang.Object; cannot be cast to [Ljava.lang.Double
The [L means an array. That is, the error is that an array of Objects cannot be cast to an array of Double. This is the same case as following:
Object[] oarr = new Object[10];
Double[] darr = (Double[]) oarr;
This is obviously not allowed.
For your issue of creating typesafe arrays, another alternative is to except a class object in init and use Array.newInstance:
import java.lang.reflect.Array;
class Foo<T> {
private T[] a ;
public Foo(Class<T> tclass) {
a = (T[]) Array.newInstance(tclass, 5);
}
public T[] getA() {
return a;
}
public static <T> Foo<T> create(Class<T> tclass) {
return new Foo<T>(tclass);
}
}
class Array1
{
public static final void main(final String[] args) {
Foo<Double> f = Foo.create(Double.class);
Double[] d = f.getA();
}
}