java.lang.ClassCastException: [Ljava.lang.Comparable; 不能投射到

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18292282/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:08:59  来源:igfitidea点击:

java.lang.ClassCastException: [Ljava.lang.Comparable; cannot be cast to

java

提问by user2692465

I got the following exception in my code: Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Comparable; cannot be cast to [LElement; at the following call:

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

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

where Element is defined as follows:

其中 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;
    }

}

}

any help is greatly appreciated!

任何帮助是极大的赞赏!

回答by Kon

That's not how polymorphism works. You cannot refer to a superclass (or interface) "object" through a subclass reference. You can, however, refer to any subclass object through the name of its implementing interface or any superclass.

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

Comparable c = new Element();

Or in general, you can remember that this is always legal:

或者一般来说,您可以记住这始终是合法的:

Object c = new String();

But this is never OK:

但这永远是不行的:

AnyClass m = new Object();

回答by Jeff Bowman

Arrays cannot be cast the same polymorphic way that classes can. Consider this code:

数组不能以类可以的相同多态方式进行转换。考虑这个代码:

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!

Naturally, this code doesn't make sense; you would be putting a Long into your heap of Elements, and that's just not right. The counterintuitive thing there is that the reverse doesn't work either:

自然,这段代码没有意义;您会将 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!

The consequence of this is that arrays can't be cast in either direction. (Generics can, but with specific and arcane rules about extendsand super; that's another matter.)

这样做的结果是无法在任一方向上投射数组。(泛型可以,但有关于extendsand 的特定和神秘的规则super;那是另一回事。)

回答by Farhad Maleki

This happens because of Java Type Erasure. To answer this question I need to explain Unbounded Wildcards, Bounded Wildcards, and Type Erasure. Feel free to skip any part if you are familiar with it.

这是因为 Java 类型擦除。要回答这个问题,我需要解释无界通配符、有界通配符和类型擦除。如果您熟悉它,请随意跳过任何部分。

The content of this post has been assembled from java documentation.

这篇文章的内容是从 java 文档中收集的。

1. Unbounded Wildcards

1. 无界通配符

The unbounded wildcard type is specified using the wildcard character (?), for example, List<?>. This is called a list of unknown type. There are two scenarios where an unbounded wildcard is a useful approach:

  • If you are writing a method that can be implemented using functionality provided in the Object class.

  • When the code is using methods in the generic class that don't depend on the type parameter. For example, List.sizeor List.clear. In fact, Class<?>is so often used because most of the methods in Class<T>do not depend on T.

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

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

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

2. Bounded Wildcards

2. 有界通配符

Consider a simple drawing application that can draw shapes such as rectangles and circles. To represent these shapes within the program, you could define a class hierarchy such as this:

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

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) {
        ...
    }
}

These classes can be drawn on a canvas:

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

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

Any drawing will typically contain a number of shapes. Assuming that they are represented as a list, it would be convenient to have a method in Canvas that draws them all:

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

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

Now, the type rules say that drawAll()can only be called on lists of exactly Shape: it cannot, for instance, be called on a List<Circle>. That is unfortunate, since all the method does is read shapes from the list, so it could just as well be called on a List<Circle>. What we really want is for the method to accept a list of any kind of shape:

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

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

There is a small but very important difference here: we have replaced the type List<Shape>with List<? extends Shape>. Now drawAll()will accept lists of any subclass of Shape, so we can now call it on a List<Circle>if we want.

List<? extends Shape>is an example of a bounded wildcard. The ?stands for an unknown type, however, in this case, we know that this unknown type is in fact a subtype of Shape. (Note: It could be Shape itself, or some subclass; it need not literally extend Shape.) We say that Shape is the upper boundof the wildcard.

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

List<? extends Shape>是一个有界通配符的例子。The?代表一个未知类型,然而,在这种情况下,我们知道这个未知类型实际上是 Shape 的一个子类型。(注意:它可以是 Shape 本身,也可以是某个子类;它不需要从字面上扩展 Shape。)我们说 Shape 是通配符的上限

Similarly, the syntax ? super T, which is a bounded wildcard, denotes an unknown type that is a supertype of T. A ArrayedHeap280, for example, includes ArrayedHeap280<Integer>, ArrayedHeap280<Number>, and ArrayedHeap280<Object>. As you can see in the java documentation for Integer class, Integer is a subclass of Number that in turn is a subclass of Object.

同样地,语法? super T,这是一个有界通配符,表示一个未知的类型,它是T.甲ArrayedHeap280的超类型,例如,包括ArrayedHeap280<Integer>ArrayedHeap280<Number>,和ArrayedHeap280<Object>。正如您在Integer classjava 文档中看到的那样,Integer 是 Number 的子类,而 Number 又是 Object 的子类。

Class Integer* java.lang.Object * java.lang.Number * java.lang.Integer

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

3. Type Erasure and ClassCastException

3. 类型擦除和 ClassCastException

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:

  • Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
  • Insert type casts if necessary to preserve type safety.
  • Generate bridge methods to preserve polymorphism in extended generic types.

During the type erasure process, the Java compiler erases all type parameters and replaces each with its first bound if the type parameter is bounded, or Object if the type parameter is unbounded.

Consider the following generic class that represents a node in a singly linked list:

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

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

在类型擦除过程中,Java 编译器擦除所有类型参数,如果类型参数有界,则用其第一个边界替换每个类型参数,如果类型参数无界,则用 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; }
    // ...
}

In the following example, the generic Node class uses a bounded type parameter:

在以下示例中,泛型 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; }
    // ...
}

The Java compiler replaces the bounded type parameter T with the first bound class, Comparable:

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

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);
    }
}

After type erasure, the Nodeand MyNodeclasses become:

类型擦除后,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);
    }
}

Consider the following code:

考虑以下代码:

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.

After type erasure, this code becomes:

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

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.

Here is what happens as the code is executed:

  • n.setData("Hello");causes the method setData(Object)to be executed on the object of class MyNode. (The MyNodeclass inherited setData(Object)from Node.)
  • In the body of setData(Object),

the

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

  • n.setData("Hello");导致在setData(Object)class 的对象上执行该方法MyNode。(MyNode类继承setData(Object)Node.)
  • 在体内setData(Object)

datafield of the object referenced by nis assigned to a String.

data引用的对象的字段n被分配给 a String

  • The datafield of that same object, referenced via mn, can be accessed and is expected to be an Integer(since mnis a MyNodewhich is a Node<Integer>.Trying to assign a Stringto an Integercauses a ClassCastExceptionfrom a cast inserted at the assignment by a Java compiler.
  • 可以访问data通过 引用的同一对象的字段,mn并且应该是 an Integer(因为mn是 a MyNode,它是 a 。Node<Integer>尝试将 a 分配String给 anInteger导致 aClassCastException来自 Java 编译器在赋值时插入的强制转换。