Простой класс:
class Box<T> {
private T t;
public Box(T t) {
this.t = t;
}
public void put(T t) {
this.t = t;
}
}
пытается выполнить метод put(), передавая экземпляр объекта Object
Box<?> box = new Box<String>("abc");
box.put(new Object());
Компилятор указывает на ошибку:
The method put(capture#1-of ?) in the type Box<capture#1-of ?> is not applicable for the arguments (Object)
Компилятор на самом деле не знает, какого типа ожидать, но одно можно сказать наверняка - это будет объект или его подкласс. Почему тогда возникает ошибка?
Спасибо
Ваш пример ясно объясняет, почему.
Фактическим типом окна является Box<String>
. Таким образом, вы действительно не хотите, чтобы в это поле помещалось нечто, отличное от экземпляров String.
Box<?>
Означает: "поле какого-то типа, неизвестного компилятору". Таким образом, вы можете получить все, что захотите, из такого поля, и вы получите экземпляры объекта, но вы можете ничего не хранить в этом поле, потому что компилятор не может гарантировать, что объект, который вы храните, имеет соответствующий тип:
class Box<T> {
private T t;
public Box(T t) {
this.t = t;
}
public void put(T t) {
this.t = t;
}
public T get() {
return t;
}
}
Box<?> box = new Box<String>("abc");
Object o = box.get(); // no problem
box.put(new Object()); // fail
Цель генериков - сделать ваш код безопасным. Если вы можете добавить произвольные объекты в поле, которое на самом деле является Box<String>
, у вас больше нет безопасности типов.
В этом учебнике указано, что:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Same compile time error as yours
"Поскольку мы не знаем, что означает тип элемента c, мы не можем добавлять к нему объекты. Метод add() принимает аргументы типа E, тип элемента коллекции. Когда фактический параметр типа есть?, Он означает какой-либо неизвестный тип. Любой параметр, который мы передаем для добавления, должен быть подтипом этого неизвестного типа. Поскольку мы не знаем, какой тип, мы не можем передать ничего. Единственное исключение - null, которое является членом каждого типа.
С другой стороны, учитывая List, мы можем вызвать get() и использовать результат. Тип результата - неизвестный тип, но мы всегда знаем, что это объект. Поэтому безопасно назначить результат get() переменной типа Object или передать его как параметр, где ожидается тип Object. "
Чтобы объяснить, в основном вы можете вызвать get(), чтобы вернуть тип, но вы не можете добавить, потому что компилятор не знает, какой тип он есть, и передаваемый параметр должен расширять неизвестный тип.