Использование универсального шаблона вместо интерфейса - PullRequest
11 голосов
/ 24 апреля 2011

Если вы хотите сохранить массив объектов типа MyInterface, допустимо ли следующее, и если да, то когда бы вы использовали вторую форму поверх первой?

i) Использование только интерфейса:-

List<MyInterface> mylist = new ArrayList<MyInterface>();

ii) Использование универсального подстановочного знака: -

List<? extends MyInterface> mylist = new ArrayList<? extends MyInterface>();

Редактировать:

Как уже указывалось в ответах, число ii не скомпилируется,В чем разница между i и случаем iii, где: -

iii) Использование универсального подстановочного знака только в ссылке: -

List<? extends MyInterface> mylist = new ArrayList<MyInterface>();

Ответы [ 5 ]

7 голосов
/ 24 апреля 2011

Второй не скомпилируется.Представьте себе:

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>();
4 голосов
/ 25 апреля 2011

List<? extends MyInterface> означает список некоторого подтипа MyInterface, но мы не знаем, какой подтип.

Поскольку мы не знаем, какой подтип на самом деле используется, мы не можем поместить в него какой-либо объект (поскольку мы не можем гарантировать, что он является элементом этого подтипа).

Мы можем вынуть объекты из такого списка и знать, что это объект, реализующий MyInterface, но не более того.

4 голосов
/ 24 апреля 2011

Если вы хотите сохранить объекты типа MyInterface, лучший (и компилируемый) подход будет следующим:

List<MyInterface> mylist = new ArrayList<MyInterface>();

Но если вы хотите использовать в параметре метода, вы можете использовать опцию 2 (ограниченный тип подстановочных знаков) для гибкости API.

public void someMethod(List<? extends MyInterface> myInterfaces);

РЕДАКТИРОВАТЬ: Приведенный выше код даст 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 класса.

2 голосов
/ 24 апреля 2011

Разница в том, что второе не скомпилируется. Вы не можете создать экземпляр параметра типа с другим параметром типа с границами.

0 голосов
/ 24 апреля 2011

использовать точную и полную информацию о типе в реализациях.

ArrayList<MyInterface> mylist = new ArrayList<MyInterface>();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...