Java 泛型 class 转换异常
Java generics class cast exception
我正在尝试创建一个 class 来处理可比对象。我归结为最简单的代码,当我尝试实例化 class 时会出错。我收到了几个编译警告(未经检查的转换),但是当我 运行 这个程序时它抛出一个 class 转换异常。我确实查看了有关该主题的其他一些问题,但没有发现有用的东西。
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>();
}
}
问题出在这里:
data = (Item[]) new Object[MAX_SIZE];
您正在实例化 Object
的数组,然后尝试将其转换为 Item
的数组,这会引发异常,因为 Object
不会扩展您的 [=14] =] class,因为它没有实现Comparable
。您想要的是:
data = new Item[MAX_SIZE];
但是你不能这样做,因为 Item
是一个通用类型。如果你想动态创建这种类型的对象(或对象数组),你需要将 Class
对象传递给你的 GD class 的构造函数:
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);
}
}
很简单:Object
没有实现 Comparable<Object>
,但是你强制执行 Item extends Comparable<Item>
。因此,您不能将 Object[]
转换为 Item[]
.
为了避免创建泛型数组Item
的问题,我们可以看看Java的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]));
}
因为你只允许插入 Item
s,所以你只能插入 return Item
s,因此你永远不会有 ClassCastException
.
关于您的代码的一些评论:通常,使用 T
、U
、S
作为类型参数。如果你使用像 Item
这样的东西,人们可能会将类型参数与实际的 class.
混淆
强制转换 (Item[])
不会根据 Item[]
检查对象的运行时类型,因为在运行时不知道 Item
是什么;相反,它会检查 Item[]
的擦除,即 Comparable[]
(因为 Comparable
是 Item
的擦除)。实际运行时间 class 为 Object[]
的对象不是 Comparable[]
的实例,因此它会导致 ClassCastException
。这就是为什么简单地做
data = (Item[]) new Comparable[MAX_SIZE];
将使异常消失。
所以有两种方法可以解决这个问题:
理论上的类型"safe"方式,即使用固定的非特定数组类型,如Object[]
或Comparable[]
作为编译时类型data
变量。这样您就可以创建一个相同运行时类型的数组对象,并将其安全地分配给变量,而无需进行不安全的强制转换。但是,当您需要从数组中取出某些内容并将其 return 作为泛型类型时,您将不得不进行显式(未经检查)转换:
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);
}
}
或者,您可以将 data
保留为 Item[]
,方法是在创建它时从 new Comparable[]
进行不安全转换。请注意,这在理论上是无效的转换,因为数组的实际运行时间 class 是 Comparable[]
,通常不是 Item[]
的实例。但是,在这个class里面,Item
被抹成了Comparable
,这个谎言不会造成任何问题,只要声称data
是Item[]
没有暴露在 class 的外部(例如,如果 data
是 public,或者如果有像 getArray()
这样的方法 returned data
直接写成Item[]
,那就不好了)。这种方式的好处是当你从数组中取出一些东西时,你不必进行强制转换。缺点是程序员要小心,不要把这个关于类型的谎言暴露给外界。
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);
}
}
我正在尝试创建一个 class 来处理可比对象。我归结为最简单的代码,当我尝试实例化 class 时会出错。我收到了几个编译警告(未经检查的转换),但是当我 运行 这个程序时它抛出一个 class 转换异常。我确实查看了有关该主题的其他一些问题,但没有发现有用的东西。
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>();
}
}
问题出在这里:
data = (Item[]) new Object[MAX_SIZE];
您正在实例化 Object
的数组,然后尝试将其转换为 Item
的数组,这会引发异常,因为 Object
不会扩展您的 [=14] =] class,因为它没有实现Comparable
。您想要的是:
data = new Item[MAX_SIZE];
但是你不能这样做,因为 Item
是一个通用类型。如果你想动态创建这种类型的对象(或对象数组),你需要将 Class
对象传递给你的 GD class 的构造函数:
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);
}
}
很简单:Object
没有实现 Comparable<Object>
,但是你强制执行 Item extends Comparable<Item>
。因此,您不能将 Object[]
转换为 Item[]
.
为了避免创建泛型数组Item
的问题,我们可以看看Java的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]));
}
因为你只允许插入 Item
s,所以你只能插入 return Item
s,因此你永远不会有 ClassCastException
.
关于您的代码的一些评论:通常,使用 T
、U
、S
作为类型参数。如果你使用像 Item
这样的东西,人们可能会将类型参数与实际的 class.
强制转换 (Item[])
不会根据 Item[]
检查对象的运行时类型,因为在运行时不知道 Item
是什么;相反,它会检查 Item[]
的擦除,即 Comparable[]
(因为 Comparable
是 Item
的擦除)。实际运行时间 class 为 Object[]
的对象不是 Comparable[]
的实例,因此它会导致 ClassCastException
。这就是为什么简单地做
data = (Item[]) new Comparable[MAX_SIZE];
将使异常消失。
所以有两种方法可以解决这个问题:
理论上的类型"safe"方式,即使用固定的非特定数组类型,如
Object[]
或Comparable[]
作为编译时类型data
变量。这样您就可以创建一个相同运行时类型的数组对象,并将其安全地分配给变量,而无需进行不安全的强制转换。但是,当您需要从数组中取出某些内容并将其 return 作为泛型类型时,您将不得不进行显式(未经检查)转换: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); } }
或者,您可以将
data
保留为Item[]
,方法是在创建它时从new Comparable[]
进行不安全转换。请注意,这在理论上是无效的转换,因为数组的实际运行时间 class 是Comparable[]
,通常不是Item[]
的实例。但是,在这个class里面,Item
被抹成了Comparable
,这个谎言不会造成任何问题,只要声称data
是Item[]
没有暴露在 class 的外部(例如,如果data
是 public,或者如果有像getArray()
这样的方法 returneddata
直接写成Item[]
,那就不好了)。这种方式的好处是当你从数组中取出一些东西时,你不必进行强制转换。缺点是程序员要小心,不要把这个关于类型的谎言暴露给外界。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); } }