我有一个使用泛型的简单类。
public class ResponseWorkerRunnable<Parameter, Result> implements Runnable {
private final ResponseWorker<Parameter, Result> worker;
/**
* The parameters for {@link ResponseWorker#doInBackground(Object...)}
*/
private final Parameter[] params;
public ResponseWorkerRunnable(ResponseWorker<Parameter, Result> worker,
Parameter... params) {
uiThreadHandler = new Handler(Looper.getMainLooper());
this.worker = worker;
this.params = params;
}
@Override
public void run() {
try {
Result res = worker.doInBackground(params);
postResultBack(res);
} catch (Exception e) {
postErrorBack(e);
}
}
}
和我的ResponseWorker:
public interface ResponseWorker<Parameter, Result> {
public Result doInBackground(Parameter... param) throws Exception;
}
问题是,我得到了ClassCastException:
java.lang.ClassCastException:java.lang.Object []无法强制转换为 model.Table []
我这样做:
Table t = new Table();
ResponseWorker<Table, SuperTable> worker = ... ;
ResponseWorkerRunnable<Table, SuperTable> r = new ResponseWorkerRunnable<Table, SuperTable>(worker, t);
将安排ResponseWorkerRunnable并将在以后运行此异常:
java.lang.ClassCastException:java.lang.Object []无法强制转换为 model.Table []
在ResponseWorkerRunnable run()方法的这一行:
Result res = worker.doInBackground(params);
我使用调试器检查Parameter [] params字段(在ResponseWorkerRunnable中)并将其设置为Object [1] {Table @ 4237c0e0}
所以它是一个对象数组,但ResponseWorker.doInBackground需要一个类Table的数组。
如何正确投射?
Result res = worker.doInBackground((Parameter[]) params);
任何其他想法或暗示可能出错的地方?
------更新-------
我使用名为ResponseWorkerExecutor的单例类调度ResponseWorkerRunnable(使用ThreadPool)
class ResponseWorkerExecutor {
public static <Parameter, Result> Future<?> submit(
ResponseWorker<Parameter, Result> responseWorker, Parameter ... params) {
return INSTANCE.executor
.submit(new ResponseWorkerRunnable<Parameter, Result>(
responseWorker, params));
}
}
所以在我的代码中我做了这样的事情: 我做这样的事情:
Table t = new Table();
// ResponseWorker implementation
ResponseWorker<Table, SuperTable> worker = ... ;
// Here is the problem with the args
ResponseWorkerExecutor.submit(worker, t);
答案 0 :(得分:2)
由于泛型在Java中的工作方式(在此处阅读type erasure),实际的Parameter
类在生成的字节码中被Object
替换,这就是为什么你的varargs数组是Object[]
而非Table[]
。
在这种情况下有一种解决方法应该可行,它涉及对代码的一些更改:
// Pass the actual class object to your Runnable, in this case t.getClass() -> Table.class
ResponseWorkerRunnable<Table, SuperTable> r = new ResponseWorkerRunnable<Table, SuperTable>(worker, t, t.getClass());
然后:
public class ResponseWorkerRunnable<Parameter, Result> implements Runnable {
private final ResponseWorker<Parameter, Result> worker;
/**
* The parameters for {@link ResponseWorker#doInBackground(Object...)}
*/
private final Parameter[] params;
private final Class<?> actualClass;
public ResponseWorkerRunnable(ResponseWorker<Parameter, Result> worker, Parameter... params, Class<?> actualClass) {
uiThreadHandler = new Handler(Looper.getMainLooper());
this.worker = worker;
this.params = params;
this.actualClass = actualClass;
}
@Override
public void run() {
try {
Result res = worker.doInBackground((Parameter[]) Arrays.copyOf(params, params.length, actualClass));
postResultBack(res);
} catch (Exception e) {
postErrorBack(e);
}
}
}
这样做是为了Object[]
并将其内容复制到一个新的真实Parameter[]
中,无论实际的Parameter
类是什么意思。然后它使用这个新数组调用varargs。
答案 1 :(得分:1)
使用“参数”代替传统的“P”会使您的代码难以阅读。这是怎么回事。 this.params的类型正确设置为Parameter []。如果将值传递给构造函数,则还将根据Parameter []进行检查。但是,您没有传递参数,因此运行时会为您创建一个空数组。不幸的是,它不够聪明,无法识别现在擦除的类型参数,因此它创建了一个Object []。我不知道它是否应该,但事实并非如此。
我理解你在做什么,这是有道理的。在构造函数中“修复”问题的一种方法是检查“params”的类型。鉴于它是一个数组,您可能无法使用instanceof。或者,您只需检查它是否为空。如果您没有收到参数[],请忽略“params”并创建一个新的,emtpy参数[]并将其指定给“this.params”。
答案 2 :(得分:0)
使用,修复它(至少就是我所说的那些;-))
public Result doInBackground(Parameter[] param);
如果修复它,则varags声明和泛型似乎存在问题。
答案 3 :(得分:0)
所以我用解决方法卖掉了这个问题。我使用List而不是Parameter []或Parameter ... params。
java.util.Collections类中已经有一些帮助方法,如:Collections.singletonList(param);
所以在我的情况下,在我看来,这是最好的解决方案,因为我只有一行代码,我必须在List&lt;&gt;中放置一个对象。或者将数组转换为列表。因此,这个方法是一个小库的一部分,库的用户不必关心它。
使用Arrays.copyOf(params,params.length,actualClass))的解决方案; @gpeche建议需要一个aditional Class作为参数,最后库的用户必须添加该类。
所以我猜我通过使用List代替参数... params
找到了妥协