2013-11-27 1 views
2

У меня есть простой класс, который использует generics.Generics: ClassCastException: java.lang.Object [] не может быть добавлен в

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 [] не может быть приведен к модель.Таблица []

Я делаю что-то вроде этого:

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() метод:

Result res = worker.doInBackground(params); 

Я использовал отладчик, чтобы проверить [] Params поле параметра (в ResponseWorkerRunnable) и его набор на объект [1] {Таблица @ 4237c0e0}

Таким образом, ее массив объекта, но ResponseWorker.doInBackground ожидает массив таблицы классов.

Как правильно его направить?

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

у вас было предупреждение, когда он был скомпилирован? – newacct

ответ

0

использования, который фиксирует его (по крайней мере, вот что сказало мое Затмение ;-))

public Result doInBackground(Parameter[] param); 

Если это исправлено, похоже, проблема с объявлением varags и generics.

1

Использование «Параметры» вместо обычного «P» делает ваш код более трудным для чтения. Что происходит, так это. Тип this.params правильно установлен на параметр []. Если вы передали значение конструктору, оно также будет проверено на Параметр []. Однако вы не передали аргумент, поэтому среда выполнения создает для вас пустой массив. К сожалению, он недостаточно умен, чтобы распознать теперь стертый параметр типа, поэтому он создает объект []. Я не знаю, нужно это или нет, но это не так.

Я понимаю, что вы делаете, и это имеет смысл. Один из способов «исправить» проблему внутри конструктора - проверить тип «params». Учитывая, что это массив, вы не сможете использовать instanceof. Или вы можете просто проверить, не пуст ли он. Если вы не получили параметр [], проигнорируйте «параметры» и создайте новый параметр emtpy [] и назначьте его «this.params».

+1

Фактически он дает его как параметр в конструкторе (таблица с именем t). Но я очень сильно согласен с вашим предложением «P». –

+0

Я обновил свой вопрос и добавил код Снайпер для ResponseWorkerExecutor – sockeqwe

+0

«К сожалению, он недостаточно умен, чтобы распознать теперь стертый тип Параметр« Это не имеет смысла. Когда он видит 'new ResponseWorkerRunnable (worker, t)', компилятор точно знает, что здесь находится «Параметр», потому что он явно указан. – newacct

2

Благодаря работе способа дженерик в Java (читать здесь о type erasure) фактическом Parameter класса заменяются Object в результате байткод, поэтому ваш массив переменных аргументов 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 с использованием этого нового массива.

+0

Тогда он также должен работать, как я предложил использовать параметр [], правильно? –

+0

@ManuelM. Насколько я знаю. он будет терпеть неудачу, если метод вызовет 'worker.doInBackground (...)' ожидает реальную таблицу [] 'вместо стираемого типа Object []'. Это часто происходит, когда вы реализуете общий интерфейс, например, этот 'ResponseWorker <...>' – gpeche

+0

Да, thats true. Я думаю о переходе из generics в Object in ResponseWorker.doInBackground (Object ... params), потому что я ненавижу дополнительный параметр для класса. С другой стороны, я ненавижу бросать params на правильный объект в doInBackground() :-) – sockeqwe

0

Так что я продал эту проблему с помощью обходного пути. Я использую List вместо Parameter [] или Parameter ... params.

В java.util.Collections уже есть некоторые методы помощи: Collections.singletonList (param);

Так что в моем случае это кажется лучшим решением, так как у меня есть только одна строка кода, где я должен поместить один объект в список <> или преобразовать массив в список. Следовательно, этот метод является частью небольшой библиотеки, которую пользователь библиотеки не должен заботиться об этом.

Решение с использованием Arrays.copyOf (params, params.length, actualClass)); предложенный @gpeche, нуждается в дополнительном параметре Class as, а в конце пользователь библиотеки должен добавить класс.

Так что я думаю, я нашел компромисс с помощью списка вместо параметра ... PARAMS

+0

Я думаю, что это в значительной степени «не решает проблему». – newacct

+0

Вы правы, это более обходной путь! – sockeqwe