问题
I am trying to create a class that processes comparables. I boiled down to the simplest code that gives an error when I try to instantiate the class. I get a couple of compile warnings (unchecked cast) but when I run this program it throws a classcast exception. I did look at some of the other questions on this topic but didnt come across something useful.
public class GD<Item extends Comparable<Item>> {
private Item[] data;
private final int MAX_SIZE = 200;
public GD() {
data = (Item[]) new Object[MAX_SIZE];
}
public static void main(String[] args) {
GD<String> g = new GD<String>();
}
}
回答1:
The problem lies here:
data = (Item[]) new Object[MAX_SIZE];
You are instantiating an array of Object
and then you try to cast it as an array of Item
, which throws an exception because Object
does not extend your Item
class, because it does not implement Comparable
. What you would like instead is:
data = new Item[MAX_SIZE];
But you can't do this because Item
is a generic type. If you want to create objects (or arrays of objects) of this type dynamically, you need to pass the Class
object to your GD class's constructor:
import java.lang.reflect.Array;
public class GD<Item extends Comparable<Item>> {
private Item[] data;
private final int MAX_SIZE = 200;
public GD(Class<Item> clazz) {
data = (Item[]) Array.newInstance(clazz, MAX_SIZE);
}
public static void main(String[] args) {
GD<String> g = new GD<String>(String.class);
}
}
回答2:
It is quite simple: Object
does not implement Comparable<Object>
, but you enforce, that Item extends Comparable<Item>
. Therefore, you cannot cast Object[]
into Item[]
.
To avoid the problem of creating an array of generic type Item
, we can take a look at Java's ArrayList implementation. Here, the backing array is of type Object
and only accessible via mutator methods:
private Object[] data; // Make the backing array private, access only via mutator methods
public void add(Item i)
{
int idx = ... // calculate next free index somehow
this.data[idx] = i;
}
public Index get(int idx) {
return ((Item) (this.data[idx]));
}
Since you only allow Item
s to be inserted, you can only return Item
s and therefore you will never have a ClassCastException
.
Some remark on your code: Typically, one uses T
, U
, S
for type parameters. If you use things like Item
, one may confuse the type parameter with an acutal class.
回答3:
The cast (Item[])
does not check the runtime type of the object against Item[]
, because what Item
is is not known at runtime; instead it checks against the erasure of Item[]
, which is Comparable[]
(because Comparable
is the erasure of Item
). An object whose actual runtime class is Object[]
is not an instance of Comparable[]
, so it causes a ClassCastException
. That's why simply doing
data = (Item[]) new Comparable[MAX_SIZE];
will make the exception go away.
So there are two ways to approach this:
The type-theoretically "safe" way, which is to use a fixed non-specific array type like
Object[]
orComparable[]
as the compile-time type of thedata
variable. That way you can create an array object of the same runtime type and safely assign it to the variable without unsafe casts. However, when you need to take something out of the array and return it as the generic type, you would have to do an explicit (unchecked) cast:public class GD<Item extends Comparable<Item>> { private Object[] data; private final int MAX_SIZE = 200; public GD() { data = new Object[MAX_SIZE]; } public Item get(int index) { return (Item)data.get(index); } }
Alternately, you could keep
data
asItem[]
, by making an unsafe cast fromnew Comparable[]
when you create it. Note that this is theoretically an invalid cast, as the array's actual runtime class isComparable[]
, which is not an instance ofItem[]
in general. However, inside this class,Item
is erased toComparable
, and this lie will not cause any problems, as long as the claim thatdata
isItem[]
is not exposed to the outside of the class (e.g. ifdata
were public, or if there was a method likegetArray()
that returneddata
directly asItem[]
, then that would not be good). The advantage of this way is that you don't have to cast when you get something out of the array. The disadvantage is that it's up to the programmer to be careful to not expose this lie about the type to the outside.public class GD<Item extends Comparable<Item>> { private Item[] data; private final int MAX_SIZE = 200; public GD() { data = (Item[])new Comparable[MAX_SIZE]; } public Item get(int index) { return data.get(index); } }
来源:https://stackoverflow.com/questions/31173283/java-generics-class-cast-exception