2009-12-03 3 views
15

Я бы хотел использовать общий список, но метод инициализации возвращает только List. Следующий код работает хорошо:Generics и вопросительный знак

List tmpColumnList = aMethodToInitializeTheColumnList(); 
tmpColumnList.add("ANICELITTLECOLUMN"); 

Java обвиняет, что я использую сырой тип, и я должен paramerize список. Итак, я добавил знак вопроса, параметризующий этот список.

List<?> tmpColumnList = aMethodToInitializeTheColumnList(); 
tmpColumnList.add("ANICELITTLECOLUMN"); 

Проблема: Теперь метод add(..) больше не работает.
Я не могу заверить, что список содержит только String, так как aMethodToInitializeTheColumnList() не реализован в мой код.

В чем моя ошибка?

Спасибо!

+1

Как это работает в первом фрагменте? Отсутствует 'new' ... – Abel

+3

Abel: вызов функции инициализирует List. Я добавлю это, чтобы прояснить эту вещь. – guerda

+0

Исправить метод инициализации? –

ответ

26

Из учебника по обобщениям. Благодаря Michael's answer!

Это не безопасно добавлять произвольные объекты к нему, однако:

Collection<?> c = new ArrayList<String>(); 
c.add(new Object()); // Compile time error 

Поскольку мы не знаем, что элемент типа С означает, что мы не можем добавить объектов к нему , Метод add() принимает аргументы типа E, тип элемента коллекции. Когда фактический параметр типа равен?, Он обозначает неизвестного типа. Любой параметр, который мы передаем , должен быть подтипом этого неизвестного типа. Поскольку мы не знаем, что это такое, мы не можем передать что угодно. Единственным исключением является null, являющийся членом одного типа.

+4

Простите мое невежество (я не парень Java). Какова цель иметь его, если он не работает? – jww

+4

@noloader: цель заключается в следующем: если у вас есть параметр метода типа 'Collection ', вы можете передать в 'Collection ', 'Collection ' или 'Collection ', и в рамках метода вы можете получить содержимое этих коллекций как type 'Object'. То, что вы не можете добавить что-либо в коллекцию, важно, поскольку оно гарантирует, что исходные ограничения типов не будут нарушены. –

17

Возможно, вы захотите использовать List<String> - так предполагается использование дженериков, т. Е. Информация о том, какие объекты будут в коллекции. Если у вас на самом деле будет список, содержащий смешанные типы (что обычно является признаком плохой конструкции), используйте List<Object>

Для получения дополнительной информации об использовании подстановочных знаков посмотрите на Generics Tutorial. Но они действительно важны только при определении ваших собственных общих классов или методов с общими параметрами.

+0

Спасибо за подсказку. Я не могу заверить, что этот список содержит только строки. – guerda

+1

В этом случае вы можете использовать List или, может быть, что-то еще, в зависимости от того, что вы знаете, список будет содержать (например, List ) – Fortega

+0

В учебнике Generics есть решение, см. Мой собственный ответ. Я подумаю о принятии вашего ответа, потому что вы дали мне подсказку. – guerda

16

Если вы используете <?>, вы имеете в виду, что вы не собираетесь использовать параметризованный тип в любом месте. Либо перейдите к определенному типу (в вашем случае это List<String>), либо к самому общему List<Object>

+0

Я не хочу указывать его, но почему тогда нормальный метод 'add' не работает? – guerda

+2

Ну, компилятор обеспечивает семантическую согласованность. Вы говорите, что вы не будете использовать параметризованный тип, поэтому он не позволит вам. Метод 'add' ожидает, что параметр, тип которого является общим параметром, поэтому вам не разрешено использовать его, если вы его не укажете. – Romain

+3

Потому что тип * неизвестен *. Компиляция не имеет возможности определить, будет ли 'String' соответствовать, поэтому вам не разрешено добавлять« String »в список. (На самом деле вам не разрешено добавлять что-либо в этот список, кроме «null».) – Bombe

2

List<?> означает, что список (или может быть) напечатан, но тип неизвестен **. Поэтому добавление чего-то к этому может быть неправильным **, если вы оказались вне этого типа. Поскольку это неизвестно, вас предупреждают.

Вы можете использовать List<Object>, что означает список, который может содержать любой тип.

+0

Я не только предупрежден, код не работает во время компиляции! – guerda

+0

@furtelwart Правда, моя формулировка была плохо выбрана :-). Но точка этой ошибки времени компиляции заключается в том, что компилятор сигнализирует, что код потенциально неверен. BTW, это целая точка подсистемы Generics, как реализовано, для поиска безопасности кода во время компиляции и добавления отбросов во время выполнения ... – KLE

+0

Простите мое невежество (я не парень Java). Какова цель иметь его, если он не работает? – jww

8

Другим вариантом в этом случае был бы объявить свой список, чтобы быть

List<? super String> 

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

Это компилируется и, на мой взгляд, немного приятнее, чем List<Object>, поскольку оно кодирует вашу неопределенность относительно того, что действительно может пойти в списке. В принципе, вы можете только добавить Строки к нему, но когда вы вызываете get(), возвращаемый элемент может быть любым (и Java правильно выведет этот тип как Object).

В практическом плане, единственное различие между этим и List<Object>, что последний позволит tmpColumnList.add(3) или tmpColumnList.add(new Thread()) и т.д. Я предпочитаю семантику он возит, а также практичности.

Смежные вопросы