Создание универсального класса типа ‹?›

Я учусь на SCJP/OCPJP и наткнулся на образец вопроса, который показался мне странным.

Пример кода создал две универсальные коллекции:

List<?> list = new ArrayList<?>();
List<? extends Object> list2 = new ArrayList<? extends Object>();

«Правильный» ответ на вопрос заключался в том, что этот код будет компилироваться, но добавление в любую коллекцию вызовет ошибку времени выполнения.

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

Collection<?> c = new ArrayList<String>();

Являются ли две общие коллекции выше законным кодом? Второй по моей логике запретил бы только интерфейсы. Первый выглядит совершенно бесполезным. Зачем использовать дженерик, который не пытается контролировать?


person jeremyjjbrown    schedule 30.12.2011    source источник
comment
Не понимаю, как это будет компилироваться, И вы можете просто добавить в обе коллекции - только ровно ОДИН объект. Я оставляю читателю в качестве упражнения выяснить, какой объект действителен для каждого класса :)   -  person Voo    schedule 30.12.2011
comment
Ты тоже не можешь. Я хотел выяснить, был ли неверным пример вопроса/ответа, и, по-видимому, это так.   -  person jeremyjjbrown    schedule 31.12.2011
comment
По какой причине только что проголосовали против?   -  person jeremyjjbrown    schedule 01.03.2014


Ответы (4)


Ознакомьтесь с отличным PDF-учебник по дженерикам Java. В частности, в разделе о подстановочных знаках содержится ответ на ваш вопрос, и я цитирую

Collection<?> c = new ArrayList<String>();
c.add( new Object() );

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

person Robin    schedule 30.12.2011
comment
Этот документ действительно очень полезен. Спасибо! Знаете ли вы какие-нибудь статьи, посвященные рефлексии или внутренним классам? - person jeremyjjbrown; 31.12.2011

Если вы хотите объявить Type во время выполнения, вы можете сделать что-то вроде этого:

public class Clazz1<T> {

private final List<T> list = new ArrayList<T>();

private List<T> getList() {
    return list;
}

/**
 * @param args
 */
public static void main(String[] args) {
    Clazz1<Integer> clazzInt = new Clazz1<Integer>();
    clazzInt.getList().add(2);
    System.out.println(clazzInt.getList());

    Clazz1<String> clazzString = new Clazz1<String>();
    clazzString.getList().add("test");
    System.out.println(clazzString.getList());
}

}
person Community    schedule 30.12.2011

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

List<?> list = new ArrayList();

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

list.add("hello world"); //This wouldn't compile
person Daniel Moses    schedule 30.12.2011

new создает конкретный экземпляр объекта. Конкретный экземпляр может иметь только один тип, включая любые дженерики. Зная это, подстановочные знаки не могут работать с new.

person barfuin    schedule 30.12.2011