Хорошо, я немного поиграл с вашим кодом и пришел к выводу.
Проблема в том, что ваш ConcreteCollection
(и его интерфейс MyCollectionInterface
) объявляют метод updateObject
как получающий аргумент типа MyObjectInterface<T>
(где T extends Number
) - обратите внимание, что тип является конкретным ( не подстановочный знак).
Теперь в вашем клиентском классе вы получаете коллекцию и сохраняете ее как MyCollectionInterface<?>
, но экземпляр, который передается конструктору ClientClass
, будет иметь конкретный тип, например:
new ClientClass(new ConcreteCollection<Integer>());
Это означает, что метод updateObject
этого экземпляра будет принимать только аргумент типа MyCollectionInterface<Integer>
.
Затем в методе foo
вы пытаетесь передать MyObjectInterface<?>
в updateObject
, но поскольку компилятор не знает, какой универсальный тип принимает ваша коллекция (это может быть Integer
, как в моем примере, но это также может быть Double
или любой другой тип, который расширяет Number
), он не допустит прохождения любого объекта .
Короче говоря, если вы объявите свою ссылку как MyCollectionInterface<?>
, вы не сможете набрать updateObject
. Итак, у вас есть два варианта:
1) Выберите конкретный тип и придерживайтесь его:
private MyCollectionInterface<Number> collection;
public ClientClass(MyCollectionInterface<Number> collection){
this.collection = collection;
}
public void foo(MyObjectInterface<Number> o){
this.collection.updateObject(o); //compiles
}
Но тогда вы ограничиваете коллекции, которые вы можете получить в конструкторе (что может быть неплохой идеей), или:
2) Измените свой интерфейс так, чтобы он принимал подстановочный тип:
public interface MyCollectionInterface<T extends Number> {
public void updateObject(MyObjectInterface<? extends Number> o);
}
public class ConcreteCollection<T extends Number> implements MyCollectionInterface<T> {
List<MyObjectInterface<T>> list;
public void updateObject(MyObjectInterface<? extends Number> o) {}
}
private MyCollectionInterface<?> collection;
public ClientClass(MyCollectionInterface<?> collection){
this.collection = collection;
}
public void foo(MyObjectInterface<?> o){
this.collection.updateObject(o); //compiles
}
Также обратите внимание, что даже в 2) вы все равно можете столкнуться с той же проблемой при реализации метода updateObject
, если вы не объявите свой list
как-то так (с ArrayList
для пример):
List<MyObjectInterface<? extends Number>> list = new ArrayList<MyObjectInterface<? extends Number>>();
В этом случае вы также можете удалить <T extends Number>
из MyCollectionInterface
и ConcreteCollection
, поскольку T
больше не используется.
@ Ваши последние вопросы:
1) Вероятно, да
2) Вы должны
3) Вы не можете, если вам действительно все равно, какие объекты вы храните в коллекции, вам следует вообще отказаться от генериков.
Извините за длинный ответ, надеюсь, это поможет.