为什么我会收到 class 转换异常?
Why am I getting a class cast exception?
我有两个 classes,AbstractArrayMyList class 和一个扩展了 AbstractArrayMyList 的 ArrayListSorted class。
这是我对 AbstractArrayMyList 和相关构造函数的声明
public abstract class AbstractArrayMyList<E extends Comparable<E>> implements MyList<E> {
private static final int DEFAULT_CAPACITY = 100;
protected E[] elementData;
public AbstractArrayMyList() {
this( DEFAULT_CAPACITY);
}
public AbstractArrayMyList( int capacity) {
elementData = (E[]) new Object[capacity];
}
MyList 是 adt 接口
还有我的 ArrayListSorted class(带有相关的构造函数),
public class ArrayListSorted<E extends Comparable<E>> extends
AbstractArrayMyList<E> {
public ArrayListSorted() {
super();
}
}
这是导致 class 转换异常的代码行。 (只是创建一个数组列表 class 排序的有界整数类型。我真的很困惑为什么会发生这个异常。
ArrayListSorted<Integer> toTestInteger = new ArrayListSorted<Integer>();
chrylis 从这里解释, 问题是 jvm 将我的新对象 [capacity] 视为对象数组。我同意这一点,但那时我的 AbstractArrayMyList 的定义仍然是
public abstract class AbstractArrayMyList<E> implements MyList<E>
,这意味着 jvm 必须将 E 视为对象,因为它对它一无所知。但是既然我添加了 E extends Comparable 难道不应该允许这种转换吗? JVM 会将其识别为可比较对象的数组?
如果 JVM 允许,您可以放置 E 以外的对象,比如 String,当您检索对象时,您假设它是 E 并将其转换为 E这将在运行时导致 ClassCastException。泛型恰恰防止了这种情况,在编译时检测尽可能多的故障。
问题是 Object 不可比较。因此在抽象 class 的构造函数中创建数组失败。
您可以通过删除抽象声明中的 Comparable
部分来解决此问题:
public abstract class AbstractArrayMyList<E> implements MyList<E> {
...
}
然后在你的子class:
public class ArrayListSorted<E extends Comparable<E>> extends AbstractArrayMyList<E> {
...
}
使一切正常。没有 class 强制转换异常。
无论如何,这最终会变得更好,因为在您将 AbstractArrayMyList
的所有子 class 限制为可比较之前。现在不必了。
更新
我现在明白了,您将使用摘要中的元素 class。在这种情况下,使您的数组类型为 Comparable:
public abstract class AbstractArrayMyList<E extends Comparable<E>> implements List<E> {
...
public AbstractArrayMyList( int capacity) {
elementData = (E[]) new Comparable[capacity];
}
...
那么您的所有排序都将起作用:
例如有这样一个主要方法:
public static void main(String... args){
ArrayListSorted<Integer> toTestInteger = new ArrayListSorted<Integer>();
toTestInteger.add(5);
toTestInteger.add(2);
System.out.println(toTestInteger.get(0));
System.out.println(toTestInteger.get(1));
}
将正确打印
2
5
(假设您的抽象 class 中的 add() 代码对数组进行求值)
更新 2
如果您在 child class 中排序,那么您将对摘要进行一些小改动 class。
完成此工作的最简单方法是抽象 class 获取通过构造函数传入的数组。这样,child classes 就构成了数组的类型。
public abstract class AbstractArrayMyList<E> implements List<E> {
protected E[] elementData;
int size=0;
protected AbstractArrayMyList(E[] elementData) {
this.elementData = elementData;
}
....
}
然后在你的 child class 中,你做默认容量的事情,然后调用 parent 的构造函数,传入你已经构造的正确类型的数组。
public class ArrayListSorted<E extends Comparable<E>> extends AbstractArrayMyList<E> {
private static final int DEFAULT_CAPACITY = 100;
public ArrayListSorted(){
this(DEFAULT_CAPACITY);
}
@SuppressWarnings("unchecked")
public ArrayListSorted(int initialSize){
//call parent constructor passing in new array
super((E[]) new Comparable[initialSize]);
}
...
现在代码又可以正常工作了
我有两个 classes,AbstractArrayMyList class 和一个扩展了 AbstractArrayMyList 的 ArrayListSorted class。
这是我对 AbstractArrayMyList 和相关构造函数的声明
public abstract class AbstractArrayMyList<E extends Comparable<E>> implements MyList<E> {
private static final int DEFAULT_CAPACITY = 100;
protected E[] elementData;
public AbstractArrayMyList() {
this( DEFAULT_CAPACITY);
}
public AbstractArrayMyList( int capacity) {
elementData = (E[]) new Object[capacity];
}
MyList 是 adt 接口
还有我的 ArrayListSorted class(带有相关的构造函数),
public class ArrayListSorted<E extends Comparable<E>> extends
AbstractArrayMyList<E> {
public ArrayListSorted() {
super();
}
}
这是导致 class 转换异常的代码行。 (只是创建一个数组列表 class 排序的有界整数类型。我真的很困惑为什么会发生这个异常。
ArrayListSorted<Integer> toTestInteger = new ArrayListSorted<Integer>();
chrylis 从这里解释,
public abstract class AbstractArrayMyList<E> implements MyList<E>
,这意味着 jvm 必须将 E 视为对象,因为它对它一无所知。但是既然我添加了 E extends Comparable 难道不应该允许这种转换吗? JVM 会将其识别为可比较对象的数组?
如果 JVM 允许,您可以放置 E 以外的对象,比如 String,当您检索对象时,您假设它是 E 并将其转换为 E这将在运行时导致 ClassCastException。泛型恰恰防止了这种情况,在编译时检测尽可能多的故障。
问题是 Object 不可比较。因此在抽象 class 的构造函数中创建数组失败。
您可以通过删除抽象声明中的 Comparable
部分来解决此问题:
public abstract class AbstractArrayMyList<E> implements MyList<E> {
...
}
然后在你的子class:
public class ArrayListSorted<E extends Comparable<E>> extends AbstractArrayMyList<E> {
...
}
使一切正常。没有 class 强制转换异常。
无论如何,这最终会变得更好,因为在您将 AbstractArrayMyList
的所有子 class 限制为可比较之前。现在不必了。
更新
我现在明白了,您将使用摘要中的元素 class。在这种情况下,使您的数组类型为 Comparable:
public abstract class AbstractArrayMyList<E extends Comparable<E>> implements List<E> {
...
public AbstractArrayMyList( int capacity) {
elementData = (E[]) new Comparable[capacity];
}
...
那么您的所有排序都将起作用:
例如有这样一个主要方法:
public static void main(String... args){
ArrayListSorted<Integer> toTestInteger = new ArrayListSorted<Integer>();
toTestInteger.add(5);
toTestInteger.add(2);
System.out.println(toTestInteger.get(0));
System.out.println(toTestInteger.get(1));
}
将正确打印
2
5
(假设您的抽象 class 中的 add() 代码对数组进行求值)
更新 2
如果您在 child class 中排序,那么您将对摘要进行一些小改动 class。
完成此工作的最简单方法是抽象 class 获取通过构造函数传入的数组。这样,child classes 就构成了数组的类型。
public abstract class AbstractArrayMyList<E> implements List<E> {
protected E[] elementData;
int size=0;
protected AbstractArrayMyList(E[] elementData) {
this.elementData = elementData;
}
....
}
然后在你的 child class 中,你做默认容量的事情,然后调用 parent 的构造函数,传入你已经构造的正确类型的数组。
public class ArrayListSorted<E extends Comparable<E>> extends AbstractArrayMyList<E> {
private static final int DEFAULT_CAPACITY = 100;
public ArrayListSorted(){
this(DEFAULT_CAPACITY);
}
@SuppressWarnings("unchecked")
public ArrayListSorted(int initialSize){
//call parent constructor passing in new array
super((E[]) new Comparable[initialSize]);
}
...
现在代码又可以正常工作了