В чем разница между? и Object в Java-дженериках?
Я использую Eclipse, чтобы помочь мне очистить некоторый код, чтобы правильно использовать дженерики Java. Большую часть времени он отлично справляется с выводом типов, но есть случаи, когда предполагаемый тип должен быть как можно более общим: Object. Но Eclipse, похоже, дает мне возможность выбирать между типом объекта и типом "?".
Итак, какая разница между:
HashMap<String, ?> hash1;
и
HashMap<String, Object> hash2;
Ответы
Ответ 1
Экземпляр HashMap<String, String>
соответствует Map<String, ?>
, но не Map<String, Object>
. Предположим, вы хотите написать метод, который принимает карты из String
на что угодно: если вы напишете
public void foobar(Map<String, Object> ms) {
...
}
вы не можете предоставить HashMap<String, String>
. Если вы пишете
public void foobar(Map<String, ?> ms) {
...
}
он работает!
То, что иногда не понято в Java-генериках, заключается в том, что List<String>
не является подтипом List<Object>
. (Но String[]
на самом деле является подтипом Object[]
, что одна из причин, почему дженерики и массивы плохо смешиваются (массивы в Java ковариантны, дженериков нет, они являются инвариантными)).
Пример:
Если вы хотите написать метод, который принимает List
из InputStream
и подтипы InputStream
, вы должны написать
public void foobar(List<? extends InputStream> ms) {
...
}
Кстати: Joshua Bloch Эффективная Java - отличный ресурс, когда вы хотите понять не очень простые вещи на Java. (Ваш вопрос выше также очень хорошо освещен в книге.)
Ответ 2
Другой способ подумать об этой проблеме состоит в том, что
HashMap<String, ?> hash1;
эквивалентно
HashMap<String, ? extends Object> hash1;
Объедините это знание с принципом "Get and Put" в разделе (2.4) из Java Generics and Collections:
Принцип Get and Put: используйте расширяет подстановочный знак, когда вы получаете значения из структуры, используйте super подстановочный знак, когда вы только ставите значения в структуру и не использовать подстановочный знак когда вы оба получаете и ставите.
и дикая карта может начать иметь больше смысла, надеюсь.
Ответ 3
Легко понять, если вы помните, что Collection<Object>
- это всего лишь общая коллекция, содержащая объекты типа Object
, но Collection<?>
- супер тип всех типов коллекций.
Ответ 4
Вы не можете безопасно помещать что-либо в Map<String, ?>
, потому что вы не знаете, каким типом должны быть значения.
Вы можете поместить любой объект в Map<String, Object>
, потому что значение известно как Object
.
Ответ 5
Ответы выше ковариации охватывают большинство случаев, но упускают одну вещь:
"?" включает в себя "Объект" в иерархии классов. Можно сказать, что String - это тип объекта, а Object - это тип?. Не все соответствует Object, но все соответствует?.
int test1(List<?> l) {
return l.size();
}
int test2(List<Object> l) {
return l.size();
}
List<?> l1 = Lists.newArrayList();
List<Object> l2 = Lists.newArrayList();
test1(l1); // compiles because any list will work
test1(l2); // compiles because any list will work
test2(l1); // fails because a ? might not be an Object
test2(l2); // compiled because Object matches Object