在使用可变参数和泛型时发生ClassCastException [英] ClassCastException while using varargs and generics
问题描述
如果我使用下面的代码,我会得到一个 奇怪的是,如果我在Android(dalvik)上运行这个函数,那么异常中不会包含任何堆栈跟踪,如果我将接口更改为抽象类,则异常变量 代码: 异常堆栈跟踪是: <这是预期的行为。在Java中使用泛型时,对象的实际类型不包含在已编译的字节码中(这称为类型擦除)。所有类型变成 ClassCastException $ c
e
为空。
public class GenericsTest {
public class Task< T> {
public void doStuff(T param,Callback< T> callback){
//这被调用,param是StringimportantStuff
//工作解决方法:
// T [] arr =(T [])Array.newInstance(param.getClass(),1);
// arr [0] = param;
//callback.stuffDone (arr);
//警告:类型安全:为可变参数
callback.stuffDone(param)创建T的通用数组;
}
}
public interface Callback< T> {
//警告:类型安全:通过varargs参数params可能造成堆污染
public void stuffDone(T ... params);
}
public void run(){
任务< String> task = new Task< String>();
try {
task.doStuff(importantStuff,new Callback< String>(){
public void stuffDone(String ... params){
//永远不会获取称为
System.out.println(params);
}});
} catch(ClassCastException e){
// e包含java.lang.ClassCastException:[Ljava.lang.Object;不能转换为[Ljava.lang.String;
System.out.println(e.toString());
public static void main(String [] args){
new GenericsTest()。run();
$ b如果你运行这个, code> ClassCastException 字符串
无效的行号。这是Java中的错误吗?我已经在Java 7和Android API 8中进行了测试。我为它做了解决方法(在 doStuff
-method中注释掉了),但似乎很愚蠢这条路。如果我删除可变参数( T ...
),一切正常,但是我的实际实现有点需要它。
java.lang.ClassCastException:[Ljava.lang.Object;不能转换为[Ljava.lang.String; (GenericsTest.java:1)
GenericsTest $ $ $ b $ GenericsTest.java:
GenericsTest.java:
(GenericsTest.java:26)$ b $ GenericsTest.main(GenericsTest.java:39)
Object
,并且强制转换插入到编译后的代码中以模拟类型化行为。 Object []
的数组。
因此,你的行 callback.stuffDone(param);
编译为 callback.stuffDone(new Object [] {param});
。但是,您的回调实现需要 String []
类型的数组。 Java编译器在您的代码中插入了一个不可见的强制转换来强制执行此类输入,并且因为 Object []
不能转换为 String []
,你会得到一个异常。您看到的虚假行号大概是因为演员没有出现在您的代码中的任何位置。
解决方法是彻底删除Callback界面中的泛型,类,用 Object
替换所有类型。
I'm using java generics and varargs.
If I use the following code, I'll get a ClassCastException
, even though I'm not using casts at all.
Stranger yet, if I run this on Android (dalvik) no stack trace is included with the exception, and if I change the interface to abstract class, the exception variable e
is empty.
The code:
public class GenericsTest {
public class Task<T> {
public void doStuff(T param, Callback<T> callback) {
// This gets called, param is String "importantStuff"
// Working workaround:
//T[] arr = (T[]) Array.newInstance(param.getClass(), 1);
//arr[0] = param;
//callback.stuffDone(arr);
// WARNING: Type safety: A generic array of T is created for a varargs parameter
callback.stuffDone(param);
}
}
public interface Callback<T> {
// WARNING: Type safety: Potential heap pollution via varargs parameter params
public void stuffDone(T... params);
}
public void run() {
Task<String> task = new Task<String>();
try {
task.doStuff("importantStuff", new Callback<String>() {
public void stuffDone(String... params) {
// This never gets called
System.out.println(params);
}});
} catch (ClassCastException e) {
// e contains "java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;"
System.out.println(e.toString());
}
}
public static void main(String[] args) {
new GenericsTest().run();
}
}
If you run this, you'll get an ClassCastException
that Object
cannot be cast to String
with stack trace pointing to invalid line number. Is this a bug in Java? I've tested it in Java 7 and Android API 8. I did workaround for it (commented out in the doStuff
-method), but it seems silly to have to do it this way. If I remove varargs (T...
), everything works OK, but my actual implementation kinda needs it.
Stacktrace from exception is:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
at GenericsTest$1.stuffDone(GenericsTest.java:1)
at GenericsTest$Task.doStuff(GenericsTest.java:14)
at GenericsTest.run(GenericsTest.java:26)
at GenericsTest.main(GenericsTest.java:39)
This is expected behaviour. When you use generics in Java, the actual types of the objects are not included in the compiled bytecode (this is known as type erasure). All types become Object
and casts are inserted into the compiled code to simulate typed behaviour.
Additionally, varargs become arrays, and when a generic varargs method is called, Java creates an array of type Object[]
with the method parameters before calling it.
Thus, your line callback.stuffDone(param);
compiles as callback.stuffDone(new Object[] { param });
. However, your implementation of the callback requires an array of type String[]
. The Java compiler has inserted an invisible cast in your code to enforce this typing, and because Object[]
cannot be cast to String[]
, you get an exception. The bogus line number you see is presumably because the cast doesn't appear anywhere in your code.
One workaround for this is to completely remove the generics from your Callback interface and class, replacing all types with Object
.
这篇关于在使用可变参数和泛型时发生ClassCastException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!