Неуниверсальная ссылка на универсальный класс приводит к неуниверсальным типам возврата - PullRequest
8 голосов
/ 16 января 2009

У меня есть унаследованный класс, который сам по себе не является универсальным, но один из его методов, возвращающий тип, использует универсальные:

public class Thing {
    public Collection<String> getStuff() { ... }
}

getStuff() использует шаблоны для возврата коллекции строк. Поэтому я могу перебрать getStuff(), и нет необходимости приводить элементы к String:

Thing t = new Thing();
for (String s: t.getStuff())  // valid
{ ... }

Однако, если я сам изменю Thing на общий, но оставлю все остальное таким же:

public class Thing<T> {
    public Collection<String> getStuff() { ... }
}

и затем продолжайте использовать неуниверсальную ссылку на Thing, getStuff() больше не возвращает Collection<String> и вместо этого возвращает нетипизированный Collection. Таким образом, код клиента не компилируется:

Thing t = new Thing();
for (String s: t.getStuff())  // compiler complains that Object can't be cast to String
{ ... }

Почему это? Какие обходные пути?

Я предполагаю, что, используя неуниверсальную ссылку на обобщенный класс, Java отключает все обобщенные элементы для всего класса. Это боль, потому что теперь я сломал свой клиентский код, сделав Thing универсальным.

Редактировать: Я делаю Thing универсальным для другого метода, который не указан в примере кода выше. У меня вопрос воспитательный, почему вышеупомянутое не может быть сделано.

Ответы [ 3 ]

10 голосов
/ 16 января 2009

Хорошо, возьми два, я неправильно понял твой вопрос.

Когда вы используете delcare Thing (это называется raw type ) вместо Thing<?> ( параметризованный тип ), компилятор Java удаляет все общие аргументы, даже если как в вашем случае) универсальный тип метода не имеет ничего общего с универсальным типом класса.

Из (отлично) Часто задаваемые вопросы по Java Generics :

Могу ли я использовать необработанный тип как любой другой тип?

Методы или конструкторы необработанного типа имеют сигнатуру, которую они имели бы после стирания типа.

Это, казалось бы, безобидное и ненавязчивое предложение описывает рассматриваемое поведение. Вы используете Thing в качестве необработанного типа, поэтому тип возвращаемого значения - Collection (не Collection<String>), поскольку это тип после стирания типа.

Confused? Неудивительно. Просто посмотрите на размер этого FAQ. Вероятно, на земле существует около трех человек, которые понимают полное значение Java Generics. Просто рассмотрите мое любимое заявление от JDK:

Enum<T extends Enum<T>> 

(Это объяснение есть и в FAQ).

0 голосов
/ 04 апреля 2012

Я думаю, это совершенно нормально. По моему мнению, используя Thing t = new Thing (); Для общего включенного класса это совершенно ошибка. Когда компилятор видит универсальный класс, используемый как класс без параметра типа, он думает, что он должен стереть все универсальные типы из этого класса. Таким образом, вы можете компилировать старые коды без использования обобщений в новых java-компиляторах, и компилятор позволяет этим старым кодам использовать обобщенные включенные классы (например, java.util.ArrayList) без каких-либо проблем. (и это то, как Java не нужно разделять System.Collection.Generic.List и System.Collection.List как C #). Вы можете запустить Thing t = new Thing();, добавив в него параметр простого типа, Thing<Object> t = new Thing<Object>();, единственное, что нужно компилятору java - это убедиться, что вы используете java generic осознанно. Я никогда не могу винить Java за его большую обратную совместимость.

Я знаю, что немного опоздал: D

0 голосов
/ 16 января 2009

Это сбой из-за стирания. Вы можете прочитать больше об этом в этих Учебниках Java

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...