Weird runtime error (ClassCastException while declaring)
I have the following code within a program I'm making:
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 }
but when I run method methodA
, I get this error:
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: [开发者_Python百科Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
at Clazz.methodA(Clazz.java:8)
Why would I get this error? Of course, the code I've shown is incomplete compared to the huge class in question (for instance, the array t
won't be empty when checking its length), but I believe I've shown everything that matters. Why won't this run?
A note: I am making this with JDK 1.7, so that's why line 7 compiles and works
Working Solution
I decided, for whatever reason, to implement the following solution, and it worked:
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 }
Though this is a solution, I would still like to know why it works.
You are missing the code that initializes T
, but I'm going to assume it looks something like this. I've added a few lines that don't change any functionality but which will help demonstrate the error:
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);
}
}
With this code I could reproduce the error. The problem here is in this code:
int l2 = ints2.t.length
Since the compiler knows the type parameter for ints2
and thus ints2.t
, this can be thought of as the rough equivalent of this:
Integer[] temp = ints2.t;
int l2 = temp.length;
It is in the implicit cast to Integer[]
(whose class simple name is [Ljava.lang.Integer
) that this fails, since t
is an Object[]
and not an Integer[]
, and one cannot be cast to the other.
Working with generic arrays
There are many complications from working with arrays declared over a generic type that are documented elsewhere. In short, I'll say that if you need to have a "generic array" instead consider declaring and using it as an Object[]
in every way, except that when you interact with a client of the class, you either accept or return only a T
instead of an Object
(for returning, via an unchecked cast). For example,
Object[] t = new Object[5];
public T getSomethingFromArray() {
return (T)t[2];
}
public void setSomethingInArray(T something) {
t[2] = something;
}
This is how ArrayList
works, by the way. Have a look at its code on DocJar.
Edit
Generic arrays aside, I don't think you understand the idea of the implicit cast. Here's much shorter code that fails with essentially the same error:
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)
...
Even though there is no need to cast clazz.t
to a String, it does so implicitly simply by referencing clazz.t
. Here is the javap -c
output for that compiled class:
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
}
In the case of your original code, here is the javap -c
output of 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
精彩评论