Есть много вопросов о проблеме объединения дженериков с varargs.Это потребует универсальных массивов, которые не существуют, когда реальный код пытается их создать.Более того, имеется большое количество документации по неопределенности предупреждений от методов varargs с параметрами non-reifiable.Из-за стирания типа это создает потенциальное загрязнение кучи, отсюда и предупреждение (в Java 6 у вызывающей стороны).Однако мой вопрос не об этих проблемах.Я думаю, что понимаю, что некоторые вещи невозможны. То, что я хотел бы знать, - это способ элегантного решения этих проблем в моем сложном случае.
Ссылки по связанным темам:
Мой случай
У меня есть BookItemSearchAddTask
, который простирается от Android AsyncTask
, но где-то в его иерархии наследования сделан общий, более абстрактный на более высоких уровнях:
На более высоком уровне это SearchAddTask
, который содержит метод start()
для выполнения задачи, вызываемый клиентом, который знает, что он передает BookItem
Продукт в.
public abstract class SearchAddTask<ProductToAdd extends Product & NamedProduct>
extends AddTask<ProductToAdd, ProductToAdd> {
public void start(ViewActivity context, ProductToAdd product) throws SpecificAddTaskDomainException, TaskExistsException, TaskUnavailableException {
super.start(context, product);
//more stuff ...
execute(product);
}
}
Уровень ниже это ItemSearchAddTask
.Здесь реализован метод doInBackground
, как того требует API AsyncTask
.Он по-прежнему может использовать дженерики.
public abstract class ItemSearchAddTask extends SearchAddTask<I> {
public I doInBackground(I... params) {
I product = params[0];
//do stuff ...
return product;
}
}
Наконец BookItemSearchAddTask
равен ItemSearchAddTask<BookItem>
.A BookItem
, следовательно, является Item
, является Product
. "I
" связан с классом, в котором находится этот вложенный класс задач ItemSearchAddTask
:
public abstract class ItemSearchAddWindow<I extends Item & ImageRepresentedProduct & NamedProduct> extends ViewActivity implements View.OnClickListener,
AdapterView.OnItemClickListener {}
Проблема
Теперь,когда я запускаю этот код, я получаю следующее ошибка :
Caused by: java.lang.ClassCastException: [Lnet.lp.collectionista.domain.Product;
at net.lp.collectionista.ui.activities.items.book.ItemSearchAddWindow$ItemSearchAddTask.doInBackground(ItemSearchAddWindow.java:1)
Обратите внимание на "[L
".
Я также получаю время компиляции предупреждения at "execute(product);
": "Type safety: A generic array of ProductToAdd is created for a varargs parameter
"
Причина
Насколько я понимаю, JVM обнаруживает, что в doInBackground
vararg он получает Product[]
передано, а не Item[]
(I[]
), ожидаемое .Помимо общих массивов, о которых трудно думать, я думаю, что происходит с клеткой льва в http://docs.oracle.com/javase/tutorial/java/generics/subtyping.html. Не по моему коду, а потому что сгенерировал универсальный массив ProductToAdd
(который в основном расширяет Product
) был создан для параметра varargs.
Я проверил, что если я передаю без аргумента в execute
, то это работает .Также использование "execute((ProductToAdd[])new MusicCDItem[]{new MusicCDItem()});
" работает в основном (не спрашивайте, MusicCDItem
- это Item
, точно так же, как BookItem
-).
Решение?
Однаков start()
я не могу знать, что мне нужно передать в BookItem
. Я знаю только о "I
".Так как это универсальный , я не могу создать универсальный массив, который требуется передать как параметр varargs.Я думаю, что сложным в моем случае являются разные уровни использования обобщений, а также параллельные иерархии.
Как мне обойти этот пробел?Я рассмотрел:
- Удаление дженериков .Немного радикально.
- Держать параметр varargs повсюду во всех универсальных битах кода (т. Е. Изменять сигнатуру метода
start()
), пока мы не достигнем кода клиента, и толькоя передаю только один элемент product, и этот элемент имеет реальный тип BookItem
.Там я получу то же предупреждение, но компилятор сможет сгенерировать правильный общий массив. - Дублирование кода
AsyncTask
и изменение doInBackground
, чтобы не использовать параметр varargs, потому что в настоящее время он мне может не понадобиться.Я предпочитаю этого не делать, поэтому я получаю преимущества при обновлении AsyncTask
в будущем. - Возможно, какой-то отражение код в
start()
.Ужасно.
Есть ли что-нибудь более короткое и локальное?
Либо это, либо в моем коде действительно глупая опечатка.