Использование общего шаблона вместо интерфейса
Если вы хотите сохранить массив объектов типа MyInterface
, являются ли следующие приемлемыми, и если да, то когда вы использовали бы вторую форму над первым?
i) Использование только интерфейса: -
List<MyInterface> mylist = new ArrayList<MyInterface>();
ii) Использование общего шаблона: -
List<? extends MyInterface> mylist = new ArrayList<? extends MyInterface>();
Edit:
Как уже указывали ответы, номер ii не будет компилироваться. В чем разница между я и случаем iii, где: -
iii) Использование общего шаблона только в ссылке: -
List<? extends MyInterface> mylist = new ArrayList<MyInterface>();
Ответы
Ответ 1
Второй не будет компилироваться. Представьте себе:
A implements MyInterface
B implements MyInterface
Затем следующее будет соответствовать вашему второму выражению, но не будет компилироваться:
// incorrect
List<A> mylist = new ArrayList<B>();
Исправление: неверно тоже:
List<? extends MyInterface> mylist = new ArrayList<MyInterface>();
Это правильно, в некотором смысле, он компилируется, но вы не можете добавлять к нему подклассы MyInterface. Сбивать с толку, но правильно - после того, как я прочитал объяснение. По той же причине: подстановочный знак можно просмотреть, например, как:
// I know this is not compileable; this is internal compiler "thinking".
// Read it as "somewhere someone may instantiate an ArrayList<A> and pass
// it down to us; but we cannot accept it as something that could be
// potentially used as List<B>"
List<A> mylist = new ArrayList<MyInterface>();
Так что это не сработает:
mylist.add(b);
и наоборот. Компилятор отказывается выполнять эти потенциально неправильные операции.
Опция, которая позволяет вам добавить любой подкласс MyInterface в mylist:
List<MyInterface> mylist = new ArrayList<MyInterface>();
Ответ 2
Если вы хотите сохранить объекты типа MyInterface
, лучший (и компилируемый) подход будет -
List<MyInterface> mylist = new ArrayList<MyInterface>();
Но если вы хотите использовать в параметре метода, вы можете использовать опцию 2 (ограниченный тип подстановочного знака) для гибкости API.
public void someMethod(List<? extends MyInterface> myInterfaces);
EDIT:
Вышеприведенный код даст более гибкий API, поскольку универсальные типы являются инвариантными, поэтому, если у вас есть -
public class A implements MyInterface {
// some implementation
}
и если someMethod
имеет тип параметра List<MyInterface>
, то он не сможет принять List<A>
, это заставляет пользователей API создавать List
с типом MyInterface
с другой стороны -
List<? extends MyInterface>
позволит передать List<A>
в someMethod
. Обычно клиент имеет List<ActualObject>
, поэтому он более гибкий, чтобы предоставить параметр, который примет любую реализацию MyInterface
В то же время не используйте подстановочные типы в качестве типов возврата, например
public List<? extends MyInterface> someMethod();
Если пользователь класса должен думать о типах подстановочных знаков, возможно, что-то не так с API классов.
Ответ 3
List<? extends MyInterface>
означает список некоторого подтипа MyInterface
, но мы не знаем, какой подтип.
Поскольку мы не знаем, какой подтип используется на самом деле, мы не можем помещать в него какой-либо объект (поскольку мы не можем гарантировать, что он является элементом этого подтипа).
Мы можем взять объекты из такого списка и знать, что это объект, реализующий MyInterface
, но не более того.
Ответ 4
Разница в том, что вторая не будет компилироваться. Вы не можете создать параметр типа с другим параметром типа с ограничениями.
Ответ 5
используйте точную и полную информацию о типе в реализациях.
ArrayList<MyInterface> mylist = new ArrayList<MyInterface>();