我确实搜索过,但没有找到类似的问题。如果这是重复的,我很抱歉。我编写了一个常规队列方法,并尝试将其扩展为具有优先级队列。我不明白为什么我只能插入超类的方法,而不能插入子类中的代码,而存储[n]是可比较的,数据也是可比较的。如果我在子类中尝试这样做,将抛出classcastexception。我做错什么了吗?
正则队列.java
import java.util.Arrays;
public class RegularQueue<T> {
protected int capacity;
protected T[] storage;
@SuppressWarnings("unchecked")
RegularQueue(int capacity) {
this.capacity = capacity;
storage = (T[]) new Object[this.capacity];
}
@Override
public String toString() {
return "Queue{" +
"capacity=" + capacity +
", storage=" + Arrays.toString(storage) +
'}';
}
void insert(T data) {
storage[0] = data;
}
}
优先级队列.java
public class PriorityQueue<T> extends RegularQueue<Comparable<T>> {
PriorityQueue(int capacity) {
super(capacity);
}
// This doesn't work
@Override
void insert(Comparable<T> data) {
storage[1] = data;
}
// ---> This works fine.
// @Override
// void insert(Comparable<T> data) {
// super.insert(data);
// }
public static void main(String[] args) {
PriorityQueue<Integer> q = new PriorityQueue<>(5);
q.insert(1);
System.out.println(q.toString());
}
}
2条答案
按热度按时间9o685dep1#
您看到这个classcastexpression是因为在regularqueue中使用的是非类型安全的赋值
storage = (T[]) new Object[this.capacity]
. 在您使用的priorityqueue中Comparable<...>
作为的类型参数T
常规队列。因此,在编译时已知T
必须在运行时Comparable
或者它的一个子类型。因此编译器发出Comparable[]
每次访问时在priorityqueue中强制转换T[] storage
来执行这个。现在的问题是
storage
不是那种类型的T[]
但只是一种类型Object[]
这导致了你看到的classcastexception。以任何方式访问字段时都会发生这种情况,甚至storage.length
触发它。为什么你没有看到这个例外
insert
方法调用super.insert
它不直接访问storage
. 只有超级实现会这样做,但是它不会执行任何强制转换,因为在regularqueue内部T
在编译时未知。解决办法是不申报
storage
作为T[]
而是使用Object[]
因为那是实际的类型。其他人向jdk团队报告了这个bug,但是这个报告(正如预期的那样)被解决为“不是问题”。然而,jdk开发人员之一stuartmarks在对报告的评论中深入解释了根本问题(可能比这个答案更好)。我强烈建议你读一读。
sg24os4d2#
这个答案将试图补充马可诺1234的答案。
使用eclipse类文件编辑器,我们可以在类文件中看到
RegularQueue.class
// Signature: <T:Ljava/lang/Object;>Ljava/lang/Object; public class RegularQueue { ... // Field descriptor #8 [Ljava/lang/Object; // Signature: [TT; protected java.lang.Object[] storage; ... // Method descriptor #56 (Ljava/lang/Object;)V // Signature: (TT;)V // Stack: 3, Locals: 2 void insert(java.lang.Object data); 0 aload_0 [this] 1 getfield RegularQueue.storage : java.lang.Object[] [19] 4 iconst_0 5 aload_1 [data] 6 aastore 7 return ...
PriorityQueue.class
// Signature: <T:Ljava/lang/Object;>LRegularQueue<Ljava/lang/Comparable<TT;>;>; public class PriorityQueue extends RegularQueue { ... // Method descriptor #19 (Ljava/lang/Comparable;)V // Signature: (Ljava/lang/Comparable<TT;>;)V // Stack: 3, Locals: 2 void insert(java.lang.Comparable data); 0 aload_0 [this] 1 getfield PriorityQueue.storage : java.lang.Object[] [22] 4 checkcast java.lang.Comparable[] [26] //<-- reason for ClassCastException 7 iconst_1 8 aload_1 [data] 9 aastore ...
checkcast
只存在于PriorityQueue
,而不是RegularQueue
它被检查为java.lang.Comparable[]
反对storage
作为擦除Comparable<T>
是Comparable
,所以storage
属于类型Comparable[]
在…看来PriorityQueue
.此外
ClassCastException
也会扔,因为PriorityQueue<T> extends RegularQueue<Number>
PriorityQueue extends RegularQueueClassCastException
不会扔(checkcast
将消失),当类型参数的类型/擦除Object
.PriorityQueue extends RegularQueue
PriorityQueue<T> extends RegularQueue<Object>
###解决方案正如马可诺1234所说,
解决方案是不将存储声明为t[],而是使用object[],因为这是实际的类型。
为了更好的类型安全性和可读性,我建议
storage
作为私人领域也提供setStorage
以及getStorage
方法:正如我们在下面的例子中看到的,
参考文献:
参考型铸造
java虚拟机指令集-checkcast