java.lang.ClassCastException:[Ljava.lang.Comparable;无法施展

时间:2013-08-17 18:55:18

标签: java

我的代码中出现以下异常: 线程“main”中的异常java.lang.ClassCastException:[Ljava.lang.Comparable;不能投[LElement; 在接下来的电话中:

Element<K,V>[] heap = (Element<K,V>[]) new Comparable[size]; 

其中Element的定义如下:

class Element<K, V> implements Comparable<Element<K, V>>{
    long timeStamp;
    K key;
    V val;
    @Override
    public int compareTo(Element<K, V> o) {
    return new Long(timeStamp).compareTo(o.timeStamp);
}
Element(long ts, K key, V val){
    this.timeStamp = ts;
    this.key = key;
    this.val = val;
    }

}

非常感谢任何帮助!

3 个答案:

答案 0 :(得分:3)

这不是多态的工作原理。您不能通过子类引用引用超类(或接口)“对象”。但是,您可以通过其实现接口的名称或任何超类来引用任何子类对象。

Comparable c = new Element();

或者一般来说,你可以记住,这总是合法的:

Object c = new String();

但这永远不行:

AnyClass m = new Object();

答案 1 :(得分:3)

这是因为Java Type Erasure。 要回答这个问题,我需要解释Unbounded Wildcards,Bounded Wildcards和Type Erasure。如果您熟悉它,请随意跳过任何部分。

这篇文章的内容是从java文档中汇总的。

1。无界通配符

  

使用通配符(?)指定无界通配符类型,例如List<?>。这称为未知类型的列表。有两种情况,无界通配符是一种有用的方法:

     
      
  • 如果您正在编写可以使用Object类中提供的功能实现的方法。

  •   
  • 当代码使用泛型类中不依赖于类型参数的方法时。例如,List.sizeList.clear。事实上,Class<?>经常被使用,因为Class<T>中的大部分方法都不依赖于T

  •   

2。有界通配符

  

考虑一个简单的绘图应用程序,可以绘制矩形和圆形等形状。要在程序中表示这些形状,您可以定义类层次结构,例如:

public abstract class Shape {
    public abstract void draw(Canvas c);
}

public class Circle extends Shape {
    private int x, y, radius;
    public void draw(Canvas c) {
        ...
    }
}

public class Rectangle extends Shape {
    private int x, y, width, height;
    public void draw(Canvas c) {
        ...
    }
}
  

可以在画布上绘制这些类:

public class Canvas {
    public void draw(Shape s) {
        s.draw(this);
   }
}
  

任何绘图通常都包含许多形状。假设它们被表示为一个列表,在Canvas中有一个方法可以方便地绘制它们:

public void drawAll(List<Shape> shapes) {
    for (Shape s: shapes) {
        s.draw(this);
   }
}
  

现在,类型规则说drawAll()只能在精确形状的列表上调用:例如,它不能在List<Circle>上调用。这是不幸的,因为所有方法都是从列表中读取形状,所以它也可以在List<Circle>上调用。我们真正想要的是接受任何形状列表的方法:

public void drawAll(List<? extends Shape> shapes) {
    ...
}
  

这里有一个很小但非常重要的区别:我们已将List<Shape>替换为List<? extends Shape>。现在drawAll()将接受Shape的任何子类的列表,因此我们现在可以根据需要在List<Circle>上调用它。

     

List<? extends Shape>是有界通配符的示例。 ?代表一种未知类型,但在这种情况下,我们知道这种未知类型实际上是Shape的子类型。 (注意:它可能是Shape本身,或者是一些子类;它不需要字面上扩展Shape。)我们说Shape是通配符的上限

类似地,语法? super T是有界通配符,表示未知类型,它是T的超类型。 例如,ArrayedHeap280包括ArrayedHeap280<Integer>ArrayedHeap280<Number>ArrayedHeap280<Object>。 正如您在java documentation for Integer class中看到的那样,Integer是Number的子类,而Number又是Object的子类。

  

类整数   * java.lang.Object      * java.lang.Number         * java.lang.Integer

3。键入Erasure和ClassCastException

  

泛型被引入到Java语言中,以便在编译时提供更严格的类型检查并支持泛型编程。为了实现泛型,Java编译器将类型擦除应用于:

     
      
  • 如果类型参数是无界的,则将泛型类型中的所有类型参数替换为其边界或对象。因此,生成的字节码只包含普通的类,接口和方法。
  •   
  • 如有必要,插入类型转换以保护类型安全。
  •   
  • 生成桥接方法以保留扩展泛型类型中的多态性。
  •   
     

在类型擦除过程中,Java编译器会擦除所有类型参数,如果type参数是有界的,则将每个参数替换为第一个绑定,如果type参数是无界的,则替换为Object。

     

考虑以下用于表示单链表中节点的泛型类:

public class Node<T> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) }
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}
```
>Because the type parameter T is unbounded, the Java compiler replaces it with Object:
```java
public class Node {

    private Object data;
    private Node next;

    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Object getData() { return data; }
    // ...
}
  

在以下示例中,通用Node类使用有界类型参数:

public class Node<T extends Comparable<T>> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}
  

Java编译器将有界类型参数T替换为第一个绑定类Comparable:

public class Node {

    private Comparable data;
    private Node next;

    public Node(Comparable data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Comparable getData() { return data; }
    // ...
}
```
> Sometimes type erasure causes a situation that you may not have anticipated. The following example shows how this can occur. 
> 
> Given the following two classes:
```java
public class Node<T> {

    public T data;

    public Node(T data) { this.data = data; }

    public void setData(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node<Integer> {
    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}
  

在类型擦除后,NodeMyNode类成为:

public class Node {

    public Object data;

    public Node(Object data) { this.data = data; }

    public void setData(Object data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}

public class MyNode extends Node {

    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}
  

请考虑以下代码:

MyNode mn = new MyNode(5);
Node n = mn;            // A raw type - compiler throws an unchecked warning
n.setData("Hello");     
Integer x = mn.data;    // Causes a ClassCastException to be thrown.
  

在类型擦除之后,此代码变为:

MyNode mn = new MyNode(5);
Node n = (MyNode)mn;         // A raw type - compiler throws an unchecked warning
n.setData("Hello");
Integer x = (String)mn.data; // Causes a ClassCastException to be thrown.
  

以下是代码执行时发生的情况:

     
      
  • n.setData("Hello");导致方法setData(Object)在类MyNode的对象上执行。 (MyNode类继承了setData(Object)的{​​{1}}。)
  •   
  • Node
  • 的正文中   
     的

setData(Object)引用的对象的

data字段已分配给n

  
      
  • 可以访问通过String引用的同一对象的data字段,并且该字段应为mn(因为Integermn这是一个MyNode。尝试将Node<Integer>分配给String会导致Java编译器在赋值时插入一个Integer
  •   

答案 2 :(得分:2)

数组不能像类那样使用相同的多态方式。请考虑以下代码:

Comparable[] foo = new Comparable[size];
foo[0] = Long.valueOf(123L);
Element<K,V>[] heap = (Element<K,V>[]) foo;

Element<K,V> thisFails = heap[0];    // this isn't safe!

当然,这段代码没有意义;你会在你的元素堆中加入一个Long,这是不对的。反直觉的是反过来也不起作用:

Element<K,V>[] heap = new Element<K,V>[];
Comparable[] foo = (Comparable[]) heap;
foo[0] = Long.valueOf(123L);
// ...which also sets heap[0], because they're two references to the same
// array object. Unlike C-style languages, arrays are objects in Java.

Element<K,V> thisFails = heap[0];    // this isn't safe!

这样做的结果是数组无法向任何方向投射。 (泛型可以,但有关extendssuper的具体而神秘的规则;这是另一回事。)