【问题标题】:Java Generics Causing Class Cast ErrorJava泛型导致类转换错误
【发布时间】:2013-05-14 15:53:54
【问题描述】:

所以我正在编写一个通用的邻接列表,我的代码没有编译错误,但是当我运行我的测试时,我得到了相同的运行时错误:

java.lang.ClassCastException: [[Ljava.lang.Object; cannot be cast to [[Lds.Graph.Edge;
    at ds.TheAdjacencyMatrix.AdjacencyMatrix.<init>(AdjacencyMatrix.java:86)
    at ds.TheAdjacencyMatrix.AdjacencyMatrix.<init>(AdjacencyMatrix.java:63)
    at ds.TheAdjacencyMatrix.AdjacencyMatrix.<init>(AdjacencyMatrix.java:73)
    at ds.Graph.Test.TheAdjacencyMatrixTest.testAddVertex(TheAdjacencyMatrixTest.java:33)

错误出现在我将二维对象数组转换为 E[][] 类型的那行的构造函数中

邻接矩阵的相关代码是::

public class AdjacencyMatrix<T, E extends Edge> 
        implements AdjacencyMatrixInterface<T, E>, Graph<T, E> {

    //~Constants----------------------------------------------
    private static final int DEFAULT_SIZE = 10;

    //~Data Fields--------------------------------------------
    /**
     * Int matrix that holds edge weights in weighted graphs. 
     * A 1 in a directed graph indicates an edge, a 0 indicates no edge.
     */
    private E[][] matrix;

    /**
     * Array of elements contained in the graph.
     * Elements correspond to the same indices as they do in the adjacency matrix of edges.
     * 
     * i.e. matrix[4][5] is an edge from 4 to 5, 
     *  elements[4] is the element at 4, elements[5] is the element at 5
     */
    private T[] elements;

    /**
     * The maximum number of vertices in the adjacency matrix.
     */
    private int size;

    /**
     * The current number of vertices in the graph.
     */
    private int numVertices;

    /**
     * Indicates whether the graph is directed or not. True if directed, false otherwise.
     */
    private boolean directed;

    //~Constructors--------------------------------------------
    /**
     * Initializes the adjacency matrix to a size of 10.
     * Which means there are 10 vertices in the graph.
     */
    public AdjacencyMatrix() {

        this(DEFAULT_SIZE);
    }

    /**
     * Initializes the adjacency matrix to a size of 10. There will be 10 vertices in the graph.
     * 
     * @param directed true if the graph is to be a directed graph, false otherwise.
     */
    public AdjacencyMatrix(boolean directed) {

        this();
        this.directed = directed;
    }

    /**
     * Initializes the adjacency matrix to a size of size.
     * There will be a maximum size of *size* vertices in the graph
     * 
     * @param size the size of the adjacency matrix.
     */
    @SuppressWarnings("unchecked")
    public AdjacencyMatrix(int size) {

        matrix = (E[][]) new Object[size][size];
        elements = (T[]) new Object[size];

        this.size = size;
        numVertices = 0;
        directed = false;
    }

而Edge类是一个抽象类,代码在这里:

package ds.Graph;

/**
 * An abstract Edge class which has methods
 * getWeight()
 * and
 * setWeight(int weight).
 * Used for a Graph data structure to abstract
 * out the edges.
 * 
 *
 *
 */
public abstract class Edge implements Comparable<Edge> {

    /**
     * Sets the weight of the edge to the passed in weight.
     * 
     * @param weight the weight of the edge.
     */
    public abstract void setWeight(int weight);

    /**
     * Gets the weight of the edge.
     * 
     * @return the edge weight.
     */
    public abstract int getWeight();
}

编辑::

所以这是在运行时设置错误的代码行。 IntEdge 只是一个继承自 Edge 的对象,它包含一个整数。

AdjacencyMatrixInterface<String, IntEdge> matrix = new AdjacencyMatrix<String, IntEdge>(false);

【问题讨论】:

  • 故事的寓意:不要在不了解为什么需要它的情况下使用@SuppressWarnings("unchecked")
  • Java 泛型不会导致此错误。你是。

标签: java generics classcastexception


【解决方案1】:

简单地将该行更改为

matrix = (E[][]) new Edge[size][size];

E 在类中被擦除到其上限。在这种情况下,E 的上限是 Edge。所以它会尝试转换为Edge[][]

此外,您必须确保 matrixelements 不会分别像 E[][]T[] 那样暴露在外部,因为它们并不是真正属于这些类型的。但只要你只在课堂内使用它们就没有问题。

【讨论】:

  • 这里的问题是E是一个抽象类。我希望能够定义从 Edge 继承的对象,然后将它们投入其中。所以我可以去抽象类或接口路由......但都给我同样的错误
  • 然后等等......所以既然 Edge[][] 是演员被替换的......那么我的问题实际上不是我正在设置某种继承的类型的引用从 Edge 引用被转换的对象数组?
【解决方案2】:

问题在于 Object[][] 不是 Edge[][] 的实例。你不能像那样投射你的物体。

new Object[][] {} instanceof Edge[][] // => false

Object[][] 的反面实际上是 Edge[][] 的超类

new Edge[][] {} instanceof Object[][] // => true

另外,根据Java Language Specification

数组类型的直接超类是Object。每个数组类型都实现了接口 Cloneable 和 java.io.Serializable。

编辑:

另外,as Rahul Bobhate pointed out,最好使用Java Collections Framework,因为它旨在利用泛型。所有array-based 解决方法都是are pretty ugly

【讨论】:

    【解决方案3】:

    这是不对的,您一定会收到未经检查的演员表警告:

    matrix = (E[][]) new Object[size][size];
            elements = (T[]) new Object[size];
    

    您需要显式传递数组实例,因为通用信息在运行时会被擦除(这意味着在运行时正在执行的代码将是 matrix = new Object[][]

    你可以有这样的构造函数:

    public class AdjacencyMatrix<T, E extends Edge> 
            implements AdjacencyMatrixInterface<T, E>, Graph<T, E> {
    E[][] matrix;
    T[] elements;
    public AdjacencyMatrix(int size, E[][] matrix, T[] elements) {
            this.matrix = matrix;
            this.elements = elements;
       }
    }
    

    【讨论】:

    • ...或使用集合代替数组我认为在这里也会有所帮助
    • 你说“泛型信息在运行时被删除”,这是真的,但是泛型类型被它们的上限替换了,所以在运行时代码变成了matrix = (Edge[][]) new Object[][],这会导致 ClassCastException。
    【解决方案4】:

    您不能将Object 的数组转换为E。虽然它会编译,但它会在运行时导致ClassCastException。您只能转换兼容的引用类型。由于E 数组和Object 数组没有任何关系,JVM 将无法将Object 数组转换为E 数组。

    您可以使用列表代替数组。您可以将矩阵定义为 ListList,如下所示:

    private List<List<E>> matrix;
    private List<T> elements;
    

    然后您可以轻松地在构造函数中初始化matrix

    public AdjacencyMatrix(int size) {
    
        matrix = new ArrayList<List<E>>(size);
        for(int i=0; i<size; i++)
        {
            t.add(new ArrayList<E>());
        }
    
        elements = new ArrayList<T>(size);
    
        this.size = size;
        numVertices = 0;
        directed = false;
    }
    

    然后您可以按如下方式访问列表中的元素:

    E e = matrix.get(0).get(1);
    

    这将为您获取第一个列表中的第二个元素。

    【讨论】:

    • "运行时会导致 ClassCastException" 不一定。 E 将被擦除到类内的上限。如果它的上限是Object,那么该语句上不会出现ClassCastException
    • ^您能详细说明一下吗?我怎样才能制作上限对象?
    【解决方案5】:

    原因是因为即使 E 是 Object 的子类,E[] 也不是 Object[] 的子类。数组具有扁平的类层次结构,因此不能相互转换。

    [编辑]

    我错了(感谢评论)。实际上数组有一个类型层次结构(参见http://etutorials.org/cert/java+certification/Chapter+6.+Object-oriented+Programming/6.5+Completing+the+Type+Hierarchy/)。正如正确指出的那样,您因此试图将超类型实例强制转换为子类型,这也不适用于任何其他 Java 对象。

    【讨论】:

    • 你倒过来了 - E[][] Object[][] 的子类,但是一个普通的 Object[][] 不能转换为 E[][] 的擦除,这是Edge[][]
    猜你喜欢
    • 2020-09-29
    • 2014-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-30
    • 1970-01-01
    相关资源
    最近更新 更多