Дженерики и знак вопроса
Я бы хотел использовать общий список, но метод инициализации возвращает List
.
Следующий код работает хорошо:
List tmpColumnList = aMethodToInitializeTheColumnList();
tmpColumnList.add("ANICELITTLECOLUMN");
Java обвиняет меня в том, что я использую raw-тип, и я должен параметризовать список.
Поэтому я добавил знак вопроса, параметризующий этот список.
List<?> tmpColumnList = aMethodToInitializeTheColumnList();
tmpColumnList.add("ANICELITTLECOLUMN");
Проблема: теперь метод add(..)
больше не работает.
Я не могу заверить, что список содержит только String
, поскольку aMethodToInitializeTheColumnList()
не реализован в моем коде.
В чем моя ошибка?
Спасибо!
Ответы
Ответ 1
Из учебника по обобщениям. Спасибо Майкл ответить!
Нельзя добавлять произвольные объекты однако:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
Так как мы не знаем, что элемент тип c означает, мы не можем добавить объекты к нему. Метод add() принимает аргументы типа E, тип элемента коллекции. Когда фактическое параметр типа?, это означает неизвестный тип. Любой параметр we pass to add должен быть подтипом этого неизвестного типа. Поскольку мы не знаете, какой тип, мы не можем пройти что угодно. Единственным исключением является null, который является членом каждого типа.
Ответ 2
Вероятно, вы хотите использовать List<String>
- то, как предполагается использовать Generics, т.е. добавлять информацию о том, какие объекты будут в коллекции. Если у вас на самом деле будет список, содержащий смешанные типы (обычно это признак плохой конструкции), используйте List<Object>
Дополнительные сведения об использовании подстановочных знаков см. в Учебное пособие по дженерикам. Но они действительно актуальны только при определении ваших собственных общих классов или методов с общими параметрами.
Ответ 3
Если вы используете <?>
, вы имеете в виду, что вы не собираетесь использовать параметризованный тип в любом месте. Либо переходите к определенному типу (в вашем случае это кажется List<String>
), либо к самому общему List<Object>
Ответ 4
Другой вариант в этом случае - объявить ваш список
List<? super String>
так как это моделирует именно то, что вы знаете об этом. Вы говорите, что не знаете точно, каковы его ограничения по типу, но из вашей второй строки справедливо предположить, что он должен содержать строки.
Это компилируется и, на мой взгляд, немного приятнее, чем List<Object>
, поскольку он кодирует вашу неопределенность относительно того, что действительно может быть в списке. В принципе, вы можете добавлять к нему только строки, но когда вы вызываете get()
, возвращаемый элемент может быть любым (и Java будет правильно вывести этот тип как Object
).
С практической точки зрения, единственная разница между этим и List<Object>
заключается в том, что последний позволит tmpColumnList.add(3)
или tmpColumnList.add(new Thread())
и т.д. Я предпочитаю семантику, которую она несет, а также практичность.
Ответ 5
List<?>
означает, что список (или может быть) указан, но тип неизвестен **. Поэтому добавление чего-то к этому может быть неправильным **, если вы оказались вне этого типа. Поскольку это неизвестно, вы предупреждаетесь.
Вы можете использовать List<Object>
, что означает список, который может содержать любой тип.