Существуют ли какие-либо планы по Java для добавления общей ковариации сбора?
Я пытался написать код, который выглядит так:
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;
}
Это работает, и, возможно, на самом деле нет никаких плохих побочных эффектов для этого. Является ли это общепринятым наилучшим подходом?
Ответы
Ответ 1
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
" - это довольно плохо, потому что, поскольку код стоит выше, он должен преуспеть.
По этой причине вы должны попытаться избежать этого метода.
Ответ 2
Это незаконно по причинам, наилучшим образом описанным в Учебник Java Generics
Взяв пример оттуда и применив его к вашему делу:
List<ConcreteObject> concreteObjects = new ArrayList<ConcreteObject>();
List<IObject> objects = concreteObjects; // assume it legal
objects.add(new IObject() { // anonymous or some other (incompatible with ConcreteObject) implementation
});
ConcreteObject co = concreteObjects.get(0); // Profit! er... I mean error
Ответ 3
Чтобы поддерживать ковариацию так, как вы ожидали, Java нуждается в "восстановленных" дженериках. Оверенный List<ConcreteObject>
знал бы, что его элементы должны быть экземплярами ConcreteObject
. Поэтому, если вызывающий объект со ссылкой на этот список, объявленный как List<IObject>
, попытался добавить AnAlternateImplObject
, операция завершилась неудачно во время выполнения с исключением — так же, как аналогичный случай с массивами сегодня бросает ArrayStoreException
.
В то время, когда были добавлены обобщения, никто не мог найти способ для подтверждения типов без нарушения совместимости с существующим кодом. Вместо этого были предоставлены "ограниченные" общие типы с использованием подстановочных знаков. Однако, если я правильно помню, с тех пор был разработан совместимый метод reification, поэтому это изменение может вернуться в таблицу для отдаленного будущего (Java 8?).
Тем временем решение, которое вы использовали, является обычным способом обработки этого случая.
Ответ 4
Просто для интереса, если вы хотите действительно педантичную и строгую обработку типов на языке, несколько похожем на Java, вы вряд ли сможете лучше, чем Scala. Барочестически сложные определения типов - вот что такое Scala. Профессор Одерский, автор Scala, является тем же парнем, который изначально создавал дженерики (робко) на Java.