2009-04-14 2 views
45

Я хочу создать массив классов, каждый из которых представляет тип, который доступен в системе, которую я строю. Все связанные классы - это подклассы общего суперкласса. Так что я хотел бы сделать:Как использовать generics с массивом классов?

Class<? extends SuperClass>[] availableTypes = { SubClass1.class, SubClass2.class }; 

Это дает мне ошибку:

Cannot create a generic array of Class<? extends SuperClass>. 

я получаю такое же сообщение при попытке квалифицировать создание массива на правой стороне инициализация:

Class<? extends SuperClass>[] availableTypes = Class<? extends SuperClass>[] { SubClass1.class, SubClass2.class }; 

я могу получить код для компиляции, если я устранить дженерики квалификацию:

Class[] availableTypes = { SubClass1.class, SubClass2.class }; 

Но тогда я получаю предупреждение о генераторах:

Класс является необработанным. Ссылки на родовой тип Class должны быть параметризованы.

Я стараюсь; Я пытаюсь! :) Кроме того, на данный момент, даже если это не вызвало предупреждения, я теряю часть интерфейса, который я пытался определить. Я не хочу просто возвращать массив произвольных классов; Я хочу вернуть массив классов, которые являются подклассами определенного SuperClass!

В Eclipse есть довольно мощные инструменты для определения параметров, которые необходимо использовать для определения объявлений дженериков, но в этом случае он падает, как это имеет место, когда вы имеете дело с классом. Предлагаемая им процедура «Infer Generic Type Arguments» не изменяет код вообще, оставляя предупреждение.

я смог обойти эту проблему, используя вместо коллекции:

List<Class<? extends SuperClass>> availableTypes = new List<Class<? extends SuperClass>>(); 

Но что это правильный способ сделать это с массивами?

ответ

22

Это кажется немного пораженческим, но проблемы, подобные этому, являются именно тем, почему большинство людей избегают смешивания массивов и дженериков. Из-за того, как реализованы дженерики (type erasure), массивы и дженерики никогда не сработают вместе.

два способа решения:

  • Стик с использованием коллекции (например, ArrayList<Class<? extends SuperClass>>), который работает так же хорошо, как массив, а также допускает расширение.
  • Помещает аннотацию @SuppressWarnings("unchecked") на код, создающий массив вместе с комментарием, оправдывающим его использование.
2

Проблема в том, что создание массива общего типа является незаконным. Единственный способ обойти это - это приведение к родовому типу при создании массива, но это не очень хорошее решение. (Обратите внимание, что можно использования общий массив, просто не создать: см. this question)

Вы должны почти всегда использовать списки вместо массивов в любом случае, так что я думаю, что вы придумали лучшее решение уже ,

14

Используйте этот синтаксис:

Class<? extends SuperClass>[] avail = new Class[] { SubClass1.class, ... }; 

Это даст вам «непроверенные» предупреждение, и это правильно, так как вы могли бы быть в том числе Class объект для такого типа, который не распространяется SuperClass в массиве.

11

Правильный способ сделать это с помощью массивов - сделать это с помощью коллекции. Сожалею! По сложному набору причин массивы не очень хорошо сочетаются с дженериками. Массивы имеют другую модель ковариации, чем общие объекты, что в конечном итоге вызывает проблемы, с которыми вы сталкиваетесь. Например, с массивами, но не (как правило) с родовыми объектами, вы можете законно сделать это:

Object[] myArray = new String[5]; 

пока вы не можете сделать это:

LinkedList<Object> myCollection = new LinkedList<String>(); 

Если вы хотите более подробно, вы можете увидеть Arrays In Java Generics страница от отличного Generics FAQ

Как сказал simonn, вы также можете использовать свои массивы так, как они есть, и используйте @SuppressWarnings("unchecked"), чтобы отключить предупреждения. Это будет функционировать, но без безопасности типа, которое генерики могут предоставить вам. Если вы беспокоитесь о производительности, просто используйте ArrayList, поэтому вы просто используете тонкую оболочку вокруг массива, но со всеми гарантиями безопасности типа, предоставляемыми дженериками.

+1

ковариационной! Вы сказали плохие слова! –

4

But what's the right way to do this with arrays?

Нет такого безопасного типа; Использование коллекции - правильный подход. Чтобы понять, почему, представьте, разрешено ли это. Вы могли бы ситуацию, как это:

// Illegal! 
Object[] baskets = new FruitBasket<? extends Citrus>[10]; 

// This is okay. 
baskets[0] = new FruitBasket<Lemon>(); 

// Danger! This should fail, but the type system will let it through. 
baskets[0] = new FruitBasket<Potato>(); 

Система типов должна обнаружить ли корзина, которая добавляется в массив имеет тип FruitBasket<? extends Citrus> или подтипа. FruitBasket не соответствует и должен быть отклонен с помощью ArrayStoreException. Но ничего не происходит!

Из-за стирания типа JVM может видеть только тип времени выполнения массива. Во время выполнения нам нужно сравнить тип массива с типом элемента, чтобы убедиться, что они совпадают. Тип компонента времени выполнения массива: FruitBasket[] после стирания типа; Аналогично, тип времени выполнения элемента - FruitBasket. Никаких проблем не обнаружено - и поэтому это опасно.

0

Не типобезопасен и непроверенным предупреждение литого:

Class<? extends SuperClass>[] availableTypes = 
    (Class<? extends SuperClass>[]) 
    (new Class[]{ SubClass1.class, SubClass2.class }); 
Смежные вопросы