Использование объекта в качестве аргумента неограниченного подстановочного объекта ссылки
Простой класс:
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)
Компилятор на самом деле не знает, какой тип ожидать, но одно точно: это будет объект или его подкласс. Почему возникает ошибка?
благодарю вас
2 ответа
Ваш пример ясно объясняет почему.
Фактический тип коробки Box<String>
, Таким образом, вы действительно не хотите иметь возможность помещать в это поле что-то кроме экземпляров String.
Box<?>
означает: "ящик некоторого типа, который неизвестен компилятору". Таким образом, вы можете получить все, что захотите, из такого ящика, и вы получите экземпляры Object, но вы не можете хранить что-либо в этом ящике, потому что компилятор не может гарантировать, что сохраняемый вами объект имеет соответствующий тип:
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, типа элемента коллекции. Когда фактический параметр типа равен?, Он обозначает некоторый неизвестный тип. Любой параметр, который мы передаем, чтобы добавить, должен был бы быть подтипом этого неизвестного типа. Так как мы не знаем, что это за тип, мы не можем ничего передать. Единственное исключение - ноль, который является членом каждого типа.
С другой стороны, учитывая List, мы можем вызвать get() и использовать результат. Тип результата - неизвестный тип, но мы всегда знаем, что это объект. Поэтому безопасно присвоить результат get() переменной типа Object или передать его в качестве параметра, где ожидается тип Object."
Для объяснения, в основном вы можете вызвать get(), чтобы вернуть тип, но вы не можете добавить, потому что компилятор не знает, какой это тип, и переданный параметр должен расширять неизвестный тип.