Почему я должен заботиться, что Java не имеет овеществленных дженериков?

Это подошло как вопрос, который я недавно задал в интервью как что-то, что кандидат хотел видеть добавленный к языку Java. Это обычно идентифицируется как боль, что Java не имеет овеществленных дженериков, но при продвижении кандидат не мог на самом деле сказать мне вид вещей, что, возможно, достиг, были они там.

Очевидно, потому что необработанные типы допустимы в Java (и небезопасные проверки), возможно ниспровергать дженерики и закончиться с a List это (например), на самом деле содержит Strings. Это ясно могло быть представлено невозможное, была овеществленная информация о типе; но должны быть больше, чем это!

Люди могли отправить примеры вещей, которые они действительно хотели бы сделать, действительно ли овеществленные дженерики были доступны? Я имею в виду, очевидно, Вы могли получить тип a List во времени выполнения - но что Вы сделали бы с ним?

public  void foo(List l) {
   if (l.getGenericType() == Integer.class) {
       //yeah baby! err, what now?

Править: Быстрое обновление этого как ответы, кажется, главным образом касается потребности передать в a Class в качестве параметра (например, EnumSet.noneOf(TimeUnit.class)). Я больше искал что-то вроде того, где это просто не возможно. Например:

List l1 = api.gimmeAList();
List l2 = api.gimmeAnotherList();

if (l1.getGenericType().isAssignableFrom(l2.getGenericType())) {
    l1.addAll(l2); //why on earth would I be doing this anyway?

101
задан oxbow_lakes 18 December 2009 в 15:39
поделиться

9 ответов

Из нескольких раз, когда я сталкивался с этой «необходимостью», она в конечном итоге сводится к следующей конструкции:

public class Foo<T> {

    private T t;

    public Foo() {
        this.t = new T(); // Help?
    }

}

Это действительно работает в C #, предполагая что T имеет конструктор по умолчанию . Вы даже можете получить тип среды выполнения с помощью typeof (T) и получить конструкторы с помощью Type.GetConstructor () .

Обычное решение Java - передать Класс в качестве аргумента.

public class Foo<T> {

    private T t;

    public Foo(Class<T> cls) throws Exception {
        this.t = cls.newInstance();
    }

}

(его необязательно передавать в качестве аргумента конструктора, поскольку аргумент метода также подходит, приведенное выше является просто примером, также try-catch опущен для краткости)

Для всех других конструкций универсального типа фактический тип может быть легко определен с небольшой помощью отражения. Приведенные ниже вопросы и ответы иллюстрируют варианты использования и возможности:

81
ответ дан 24 November 2019 в 04:36
поделиться

Что меня чаще всего раздражает, так это невозможность использовать преимущества множественной диспетчеризации для нескольких общих типов. Следующее невозможно, и есть много случаев, когда это было бы лучшим решением:

public void my_method(List<String> input) { ... }
public void my_method(List<Integer> input) { ... }
99
ответ дан 24 November 2019 в 04:36
поделиться

На ум приходит типовая безопасность . Преобразование в параметризованный тип всегда будет небезопасным без овеществленных дженериков:

List<String> myFriends = new ArrayList();
myFriends.add("Alice");
getSession().put("friends", myFriends);
// later, elsewhere
List<Friend> myFriends = (List<Friend>) getSession().get("friends");
myFriends.add(new Friend("Bob")); // works like a charm!
// and so...
List<String> myFriends = (List<String>) getSession().get("friends");
for (String friend : myFriends) print(friend); // ClassCastException, wtf!? 

Кроме того, абстракции будут просачиваться меньше - по крайней мере, те, которые могут быть заинтересованы в информации времени выполнения об их параметрах типа. Сегодня, если вам нужна какая-либо информация времени выполнения о типе одного из универсальных параметров, вы также должны передать его Class . Таким образом, ваш внешний интерфейс зависит от вашей реализации (независимо от того, используете ли вы RTTI для своих параметров или нет).

34
ответ дан 24 November 2019 в 04:36
поделиться

Мое знакомство с Java Geneircs довольно ограничено, и помимо пунктов, о которых уже упоминалось в других ответах, есть сценарий, описанный в книге Java Generics and Collections , Морис Нафталина и Филиппа Уолдера, где используются овеществленные дженерики.

Поскольку типы не реифицируемы, невозможно иметь параметризованные исключения.

Например, объявление формы ниже недействительно.

class ParametericException<T> extends Exception // compile error

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

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

11
ответ дан 24 November 2019 в 04:36
поделиться

Это не значит, что вы добьетесь чего-то экстраординарного. Так будет проще понять. Стирание типов кажется трудным временем для новичков, и в конечном итоге требует понимания того, как работает компилятор.

Я считаю, что обобщенные типы - это просто дополнительное , которое избавляет от большого количества избыточных приведений.

2
ответ дан 24 November 2019 в 04:36
поделиться

Одна хорошая вещь - избегать боксов для примитивных (значений) типов. Это в некоторой степени связано с жалобой на массивы, которую подняли другие, и в случаях, когда использование памяти ограничено, это может действительно иметь существенное значение.

Есть также несколько типов проблем при написании фреймворка, когда можно размышлять над параметризованный тип важен. Конечно, это можно обойти, передав объект класса во время выполнения, но это скрывает API и накладывает дополнительную нагрузку на пользователя инфраструктуры.

5
ответ дан 24 November 2019 в 04:36
поделиться

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

8
ответ дан 24 November 2019 в 04:36
поделиться

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

API выглядит как Iterator , где T - некоторый тип, который может быть построен с использованием только строк в конструкторе. Затем Iterator просматривает строки, возвращаемые из запроса sql, и затем пытается сопоставить их с конструктором типа T.

В текущем способе реализации универсальных шаблонов я также должен передать класс объектов, которые Я буду создавать из своего набора результатов. Если я правильно понимаю, если бы дженерики были реифицируются, я мог бы просто вызвать T.getClass () и получить его конструкторы, и тогда не пришлось бы приводить результат Class.newInstance (), что было бы намного аккуратнее.

В принципе,

5
ответ дан 24 November 2019 в 04:36
поделиться

Вы сможете создавать универсальные массивы в своем коде.

public <T> static void DoStuff() {
    T[] myArray = new T[42]; // No can do
}
26
ответ дан 24 November 2019 в 04:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: