Есть ли у Java планы по добавлению универсальной ковариации коллекции? - PullRequest
2 голосов
/ 17 ноября 2009

Я пытался написать код, который выглядел так:

public List<IObject> getObject(){
  ArrayList<ConcreteObject> objects = new ArrayList<ConcreteObject>();
  return objects;
}

(где ConcreteObject реализует IObject)

Это не работает вообще. Это дает ошибку компилятора. У Java есть планы поддержать это в будущем? Какой лучший обходной путь до тех пор? Я закончил тем, что сделал:

public List<IObject> getObject(){
  List<IObject> objects = new ArrayList<IObject>();
  return objects;
}

Это работает, и, возможно, нет никаких плохих побочных эффектов от этого. Это общепринятый лучший подход?

Ответы [ 4 ]

11 голосов
/ 17 ноября 2009

Java уже поддерживает эту функцию, вам просто нужно ее использовать. Для начинающего читайте учебник Wildcards от Sun.

Вам нужно следующее:

public List<? extends IObject> getObject(){
  ArrayList<ConcreteObject> objects = new ArrayList<ConcreteObject>();
  return objects;
}

В качестве альтернативы может использоваться небезопасное приведение:

public <T extends IObject> List<T> getObject(){
  ArrayList<T> objects = (ArrayList<T>) new ArrayList<ConcreteObject>();
  return objects;
}

… но этот метод довольно хрупкий и выдает исключение времени выполнения (за исключением сообщения об ошибке компиляции) при попытке доступа к его элементам с недопустимым типом:

@SuppressWarnings("unchecked")
public <T extends IObject> List<T> getObject(){
  ArrayList<T> objects = (ArrayList<T>) new ArrayList<ConcreteObject>();
  objects.add(new ConcreteObject());
  return objects;
}

…
List<OtherConcreteObject> objects = getObject(); // Works.
OtherConcreteObject obj = OtherConcreteObject.get(0); // Throws CCE.

Это приведет к следующему ClassCastException во время выполнения: «ConcreteObject не может быть приведен к OtherConcreteObject», что довольно плохо, потому что, поскольку код стоит выше, он должен завершиться успешно.

По этой причине вам следует избегать этого метода.

5 голосов
/ 17 ноября 2009

Это недопустимо по причинам, лучше всего описанным в Обучающее руководство по Java Generics

Взяв оттуда пример и применив его к вашему делу:

List<ConcreteObject> concreteObjects = new ArrayList<ConcreteObject>();
List<IObject> objects = concreteObjects; // assume it's legal
objects.add(new IObject() { // anonymous or some other (incompatible with ConcreteObject) implementation
});
ConcreteObject co = concreteObjects.get(0); // Profit! er... I mean error
3 голосов
/ 17 ноября 2009

Чтобы поддерживать ковариацию так, как вы ожидали, Java нуждается в «усовершенствованных» обобщениях. Усовершенствованный List<ConcreteObject> будет знать, что его элементы должны быть экземплярами ConcreteObject. Таким образом, если вызывающий со ссылкой на этот список, объявленный как List<IObject>, попытается добавить AnAlternateImplObject, операция завершится с ошибкой во время выполнения с исключением & mdash; точно так же, как аналогичный случай с массивами выдает ArrayStoreException сегодня.

В то время, когда были добавлены дженерики, никто не мог придумать способ преобразования типов без нарушения совместимости с существующим кодом. Вместо этого были предоставлены «ограниченные» универсальные типы с использованием подстановочных знаков. Однако, если я правильно помню, с тех пор был разработан совместимый метод реификации, так что это изменение может вернуться на стол в далеком будущем (Java 8?).

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

1 голос
/ 17 ноября 2009

Просто для интереса, если вам нужна действительно педантичная и строгая обработка типов на языке, несколько похожем на Java, вы вряд ли сможете добиться большего успеха, чем Scala . Сложные определения типа барокко - вот что такое Scala. Профессор Одерски, автор Scala, тот же самый парень, который изначально встраивал дженерики (более робко) в Java.

...