Использование общего шаблона вместо интерфейса

Если вы хотите сохранить массив объектов типа 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>();