奇怪的运行时错误(声明时ClassCastException)

Sup*_*tar 1 java classcastexception

我正在制作的程序中有以下代码:

01  public class Clazz<T>
02  {
03    T[] t;
04    
05    public Clazz<T> methodA(int... ints)
06    {
07      Clazz<Integer> ints2 = new Clazz<>();
08      int remInd[] = new int[t.length - ints2.t.length];
09      return this;
10    }
11  }
Run Code Online (Sandbox Code Playgroud)

但是当我运行方法时methodA,我收到此错误:

Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
    at Clazz.methodA(Clazz.java:8)
Run Code Online (Sandbox Code Playgroud)

为什么我会收到此错误?当然,与所讨论的巨大类相比,我所显示的代码是不完整的(例如,t在检查其长度时,数组不会为空),但我相信我已经展示了重要的一切.这为什么不运行?

注意:我使用JDK 1.7进行此操作,这就是为什么第7行编译和工作的原因

工作方案


无论出于何种原因,我决定实施以下解决方案,并且它有效:

01  public class Clazz<T>
02  {
03    T[] t;
04    
05    public Clazz<T> methodA(int... ints)
06    {
07      Clazz<Integer> ints2 = new Clazz<>();
08      int remInd[] = new int[t.length - ints2.length()];
09      return this;
10    }
11    
12    public int length()
13    {
14      return t.length;
15    }
16  }
Run Code Online (Sandbox Code Playgroud)

虽然这是一个解决方案,但我仍然想知道它的工作原理.

Mar*_*ers 7

你缺少初始化的代码T,但我会假设它看起来像这样.我添加了几行不会改变任何功能,但有助于演示错误:

public class Clazz<T> {
    T[] t = (T[]) new Object[5];

    public Clazz<T> methodA(int... ints) {
        Clazz<Integer> ints2 = new Clazz<Integer>();
        int l1 = t.length;
        int l2 = ints2.t.length;
        int remInd[] = new int[l1 - l2];
        return this;
    }

    public static void main(String...args) {
        Clazz<String> clazz = new Clazz<String>();
        clazz.methodA(54, 7);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用此代码,我可以重现错误.这里的问题是在这段代码中:

int l2 = ints2.t.length
Run Code Online (Sandbox Code Playgroud)

由于编译器知道类型参数ints2因此ints2.t,因此可以将其视为粗略等效于此:

Integer[] temp = ints2.t;
int l2 = temp.length;
Run Code Online (Sandbox Code Playgroud)

它是隐式转换Integer[](它的类简单名称[Ljava.lang.Integer),它失败了,因为它t是一个Object[]而不是一个Integer[],而且不能转换为另一个.

使用通用数组

使用在其他地方记录的泛型类型声明的数组有许多复杂情况.总之,我会说,如果你需要有一个"通用阵列",而不是考虑声明和使用它作为一个Object[]以各种方式,除非当你与类的客户端交互,你要么接受或只返回T代替的Object(用于返回,通过一个未检查的铸件).例如,

Object[] t = new Object[5];

public T getSomethingFromArray() {
    return (T)t[2];
}

public void setSomethingInArray(T something) {
    t[2] = something;
}
Run Code Online (Sandbox Code Playgroud)

ArrayList顺便说一下,这是如何工作的.看看DocJar的代码.

编辑

抛开通用数组,我认为你不理解隐式转换的想法.这是更短的代码,失败的基本相同的错误:

public class Clazz<T> {
    T t = (T) new Object();

    public static void main(String...args) {
        Clazz<String> clazz = new Clazz<String>();
        clazz.t.toString();
    }
}

Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
    at Clazz.main(Clazz.java:6)
    ...
Run Code Online (Sandbox Code Playgroud)

即使不需要转换clazz.t为String,它也只是通过引用 隐式地执行clazz.t.以下是该javap -c编译类的输出:

Compiled from "Clazz.java"
public class Clazz extends java.lang.Object{
java.lang.Object t;

public Clazz();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   new #2; //class java/lang/Object
   8:   dup
   9:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   12:  putfield    #3; //Field t:Ljava/lang/Object;
   15:  return

public static void main(java.lang.String[]);
  Code:
   0:   new #4; //class Clazz
   3:   dup
   4:   invokespecial   #5; //Method "<init>":()V
   7:   astore_1
   8:   aload_1
   9:   getfield    #3; //Field t:Ljava/lang/Object;
//BELOW is the line that will fail
   12:  checkcast   #6; //class java/lang/String
   15:  invokevirtual   #7; //Method java/lang/String.toString:()Ljava/lang/String;
   18:  pop
   19:  return

}
Run Code Online (Sandbox Code Playgroud)

对于原始代码,这里是javap -c输出methodA():

public Clazz methodA(int[]);
  Code:
   0:   new #5; //class Clazz
   3:   dup
   4:   invokespecial   #6; //Method "<init>":()V
   7:   astore_2
   8:   aload_0
   9:   getfield    #4; //Field t:[Ljava/lang/Object;
   12:  arraylength
   13:  aload_2
   14:  getfield    #4; //Field t:[Ljava/lang/Object;
//BELOW is the line that will fail
   17:  checkcast   #7; //class "[Ljava/lang/Integer;"
   20:  arraylength
   21:  isub
   22:  newarray int
   24:  astore_3
   25:  aload_0
   26:  areturn
Run Code Online (Sandbox Code Playgroud)