简体   繁体   English

Java泛型导致类转换错误

[英]Java Generics Causing Class Cast Error

So I'm coding up a generic adjacency list and my code has no compile errors, but when I run my tests I get the same runtime error across the board: 所以我编写了一个通用的邻接列表,我的代码没有编译错误,但是当我运行我的测试时,我得到了相同的运行时错误:

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)

The error is in the constructor on the line where I cast the 2d object array to E[][] type 错误发生在我将2d对象数组转换为E [] []类型的行的构造函数中

The relevant code for the adjacency matrix is:: 邻接矩阵的相关代码是::

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

And the Edge class is an abstract class whose code is here: 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();
}

EDIT:: 编辑::

So this is the line of code that sets of the error at runtime. 所以这是在运行时设置错误的代码行。 IntEdge is just an object inheriting from Edge that holds an integer. IntEdge只是一个继承自Edge的对象,它包含一个整数。

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

Simple change that line to 简单的改变就行了

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

E is erased to its upper bound inside the class. E被删除到班级内部。 E 's upper bound is Edge in this case. 在这种情况下, E的上限是Edge So it will try to cast to Edge[][] . 所以它会尝试强制转换为Edge[][]

Also, you have to make sure that matrix and elements is not exposed to the outside as E[][] and T[] respectively, since they are not really of those types. 此外,您必须确保matrixelements不会分别作为E[][]T[]暴露给外部,因为它们实际上并不属于这些类型。 But as long as you only use them within the class, there is no problem. 但只要你只在课堂上使用它们,就没有问题。

The issue is that Object[][] is not an instance of Edge[][]. 问题是Object [] []不是Edge [] []的实例。 You can't cast your objects like that. 你无法像那样投射你的物体。

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

Its the other way around Object[][] is in fact superclass of Edge[][] 对象[] []的另一种方式实际上是Edge [] []的超类

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

Also, According to Java Language Specification 另外,根据Java语言规范

The direct superclass of an array type is Object. 数组类型的直接超类是Object。 Every array type implements the interfaces Cloneable and java.io.Serializable. 每个数组类型都实现了Cloneable和java.io.Serializable接口。

Edit: 编辑:

Also, as Rahul Bobhate pointed out , its better to use Java Collections Framework since it was designed to utilize generics. 此外, 正如Rahul Bobhate指出的那样 ,使用Java Collections Framework更好,因为它旨在利用泛型。 All array-based workarounds are are pretty ugly . 所有基于数组的解决方法都非常难看

This is not right, you must be getting unchecked cast warning: 这不对,你必须得到未经检查的投射警告:

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

You need to explicitly pass array instances since generic information is erased at runtime (which means at runtime code under execution would be matrix = new Object[][] ) 您需要显式传递数组实例,因为通用信息在运行时被擦除(这意味着在运行时执行的代码将是matrix = new Object[][]

You can have constructor like: 你可以有像这样的构造函数:

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

You cannot cast an array of Object to an E . 您无法将Object数组转换为E Although it will compile, it will cause ClassCastException at runtime. 虽然它会编译,但它会在运行时导致ClassCastException You can cast only compatible reference types. 您只能转换兼容的引用类型。 Since an array of E and array of Object are not related in any way, JVM won't be able to cast the array of Object to array of E . 由于E数组和Object数组之间没有任何关系,因此JVM将无法将Object数组转换为E数组。

You can use List instead of array. 您可以使用List而不是数组。 You could define matrix as a List of List as follows: 您可以将矩阵定义为List List ,如下所示:

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

You can then initialize matrix in the constructor easily: 然后,您可以轻松地在构造函数中初始化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;
}

You can then access the elements inside the list as follows: 然后,您可以按如下方式访问列表中的元素:

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

This will fetch you the 2nd element in the 1st list. 这将获取第一个列表中的第二个元素。

The reason is because even though E is a subclass of Object, E[] is not a subclass of Object[]. 原因是因为即使E是Object的子类,E []也不是Object []的子类。 Arrays have a flat class hierarchy and thus can't be cast into each other. 数组具有平坦的类层次结构,因此不能相互转换。

[Edit] [编辑]

I was wrong (thx for the comment). 我错了(thx评论)。 Actually there is a type hierarchy for arrays (see http://etutorials.org/cert/java+certification/Chapter+6.+Object-oriented+Programming/6.5+Completing+the+Type+Hierarchy/ ). 实际上,数组有一个类型层次结构(参见http://etutorials.org/cert/java+certification/Chapter+6.+Object-oriented+Programming/6.5+Completing+the+Type+Hierarchy/ )。 As correctly pointed out, you are thus trying to cast a supertype instance to a subtype, which won't work for any other Java Objects as well. 正确地指出,您正在尝试将超类型实例强制转换为子类型,这也不适用于任何其他Java对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM