Java 重写超级';s法(可比<;T>;)
我确实搜索过,但没有找到类似的问题。如果这是重复的,我很抱歉。我编写了一个常规队列方法,并尝试将其扩展为具有优先级队列。我不明白为什么我只能使用super类的方法插入,而不能插入子类中的代码,而存储[n]是可比较的,数据也是可比较的。如果我在子类中尝试这样做,将抛出ClassCastException。我做错什么了吗 RegularQueue.javaJava 重写超级';s法(可比<;T>;),java,generics,comparable,Java,Generics,Comparable,我确实搜索过,但没有找到类似的问题。如果这是重复的,我很抱歉。我编写了一个常规队列方法,并尝试将其扩展为具有优先级队列。我不明白为什么我只能使用super类的方法插入,而不能插入子类中的代码,而存储[n]是可比较的,数据也是可比较的。如果我在子类中尝试这样做,将抛出ClassCastException。我做错什么了吗 RegularQueue.java import java.util.Arrays; public class RegularQueue<T> { prot
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;
}
}
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());
}
}
导入java.util.array;
公共类正则队列{
受保护的内部容量;
受保护的T[]存储;
@抑制警告(“未选中”)
常规队列(整数容量){
这个。容量=容量;
存储=(T[])新对象[this.capacity];
}
@凌驾
公共字符串toString(){
返回“队列{”+
“容量=”+容量+
“,storage=“+Arrays.toString(存储)+
'}';
}
无效插入(T数据){
存储器[0]=数据;
}
}
PriorityQueue.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;
}
}
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());
}
}
公共类优先级队列扩展了RegularQueue{
优先级队列(整数容量){
超级(容量);
}
//这不管用
@凌驾
空白插入(可比数据){
存储器[1]=数据;
}
//--这个很好用。
//@覆盖
//空白插入(可比数据){
//超级.插入(数据);
// }
公共静态void main(字符串[]args){
PriorityQueue q=新的PriorityQueue(5);
q、 插入(1);
System.out.println(q.toString());
}
}
您看到这个ClassCastExpression是因为在RegularQueue中您正在使用非类型安全赋值存储=(T[])新对象[this.capacity]
。在PriorityQueue中,您使用Comparable
作为RegularQueue的T
的类型参数。因此,在编译时已知,此T
在运行时必须是可比的
或其子类型。因此,每次您访问T[]存储时,编译器都会在PriorityQueue内发出Comparable[]
强制转换以强制执行此操作。
现在的问题是,storage
实际上不是类型T[]
,而是类型Object[]
,这导致了您看到的ClassCastException。以任何方式访问字段时都会发生这种情况,甚至storage.length
会触发它
在调用super.insert
的insert
方法中没有看到此异常的原因是它没有直接访问存储
。只有超级实现会执行此操作,但不会执行任何强制转换,因为在RegularQueue内部,T
的类型在编译时是未知的
解决方案是不要将存储声明为T[]
,而是使用Object[]
,因为这是实际的类型
其他人向JDK团队报告了这个bug,但报告(如预期的那样)已被解决为“不是问题”。然而,JDK开发人员之一斯图尔特·马克斯(Stuart Marks)在报告中深入解释了根本问题(可能比这个答案更好)。我强烈建议您阅读。此答案将尝试补充马可诺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
...
// 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
中,而不存在regularlqueue
它根据存储
检查java.lang.Comparable[]
,因为Comparable
的擦除是Comparable
,所以存储
在优先级队列
视图中属于Comparable[]
类型
另外
ClassCastException
也将抛出
PriorityQueue扩展了RegularQueue
PriorityQueue扩展了RegularQueue
当type参数的type/erase为Object
时,ClassCastException
不会抛出(checkcast
将消失)
PriorityQueue扩展了RegularQueue
PriorityQueue扩展了RegularQueue
解决方案
正如马可诺1234所说
解决方案是不将存储声明为T[],而是使用对象[],因为它是实际类型
为了更好的类型安全性和可读性,我建议将storage
也作为私有字段,并提供setStorage
和getStorage
方法:
protected void setStorage(int index, T data) {
storage[index] = data;
}
@SuppressWarnings("unchecked")
protected T getStorage(int index) {
return (T) storage[index];
}
正如我们在下面的例子中所看到的
public class PriorityQueue<T> extends RegularQueue<Comparable<T>> {
...
@Override
void insert(Comparable<T> data) {
setStorage(1, new Object()); // Compile error
// following is allowed if storage is protected, error only occur when casting the value to Comparable<T>
// storage[1] = new Object();
}
public Comparable<T> getByIndex(int index) {
return getStorage(index);
// Need to repeatedly cast when using storage value
// return (Comparable<T>) storage[index];
}
...
公共类优先级队列扩展了RegularQueue{
...
@凌驾
空白插入(可比数据){
设置存储(1,新对象());//编译错误
//如果存储受到保护,则允许执行以下操作,仅当将值强制转换为可比值时才会发生错误
//存储器[1]=新对象();
}
公共可比getByIndex(int索引){
返回getStorage(索引);
//使用存储值时需要重复强制转换
//返回(可比)存储[索引];
}
...
参考资料:
问题是,编译器实际上不需要将存储
转换为可比[]
,因为存储
的唯一用途是设置它的一个元素,这可以在对象[]上完成
。编译器选择在优先级队列
中使用存储时插入强制转换,即使我们不使用存储
是T[]
这一事实,并且此编译器决定是可接受的,但不是强制性的。可能其他编译器不会在此处插入强制转换。