Что такое использование дженериков в Java? X.‹Y›метод()

Я прочитал всю книгу SCJP6 Sierra and Bates book, набрал 88% экзамена.

Но, тем не менее, я никогда не слышал о том, как работает такой код, поскольку он не объясняется в главе о дженериках:

Collections.<TimeUnit>reverseOrder()

Что такое использование дженериков? Я обнаружил это в каком-то коде, но ничего об этом не читал. Мне кажется, это позволяет оказать некоторую помощь в выводе типов. Я пытался искать об этом, но это не так просто найти (и это даже не в книге / экзамене SCJP!)

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

Спасибо


Редактировать Спасибо за ответы, но я ожидал более подробной информации :) поэтому, если кто-то хочет добавить дополнительную информацию:

Как насчет более сложных случаев, таких как

  • Используя тип, объявленный в классе, могу ли я сделать что-то вроде Collections.<T>reverseOrder(), например?
  • Используете extends, super?
  • Использование ?
  • Предоставление компилятору только частичной помощи (т.е. O.manyTypesMethod<?,MyHelpTypeNotInfered,?,?,?,?,?>() )

person Sebastien Lorber    schedule 08.06.2012    source источник


Ответы (5)


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

См. пример в конце страницы руководства.

Обновление: допустим только первый из ваших примеров. Аргумент явного типа должен быть, ну, явным, поэтому там не допускаются подстановочные знаки, extends или super. Более того, либо вы явно указываете каждый аргумент типа, либо ни один из них; т. е. количество явных аргументов типа должно совпадать с количеством параметров типа вызываемого метода. Параметр типа, такой как T, разрешен, если он хорошо определен в текущей области, например. как параметр типа окружающего класса.

person Péter Török    schedule 08.06.2012

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

person ZoFreX    schedule 08.06.2012

Просто небольшое дополнение к другим ответам.

При получении соответствующей ошибки компилятора:

В то время как «традиционный» подход к кастингу

(Comparator<TimeUnit>) Collections.reverseOrder()

похоже на подход дженериков

Collections.<TimeUnit>reverseOrder()

подход к приведению, конечно, не является типобезопасным (возможное исключение во время выполнения), в то время как подход с использованием дженериков создаст ошибку компиляции, если возникнет проблема. Таким образом, подход с использованием дженериков, конечно, предпочтительнее.

person Puce    schedule 08.06.2012

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

Например, рассмотрим методы Collections.empty*, которые возвращают пустую коллекцию. Если у вас есть метод, который ожидает Map<String, String>:

public static void foo(Map<String, String> map) { }

Вы не можете напрямую передать ему Collections.emptyMap(). Компилятор будет жаловаться, даже если он знает, что ожидает Map<String, String>:

// This won't compile.
foo(Collections.emptyMap());

Вы должны явно объявить нужный тип в вызове, что, на мой взгляд, выглядит довольно уродливо:

foo(Collections.<String, String>emptyMap());

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

Map<String, String> map = Collections.emptyMap();
foo(map);

Это может показаться не очень важным, но когда универсальные типы начинают становиться более сложными (например, Map<String, List<SomeOtherGenericType<Blah>>>), некоторые начинают желать, чтобы в Java был более интеллектуальный вывод типов (но, поскольку это не так, вероятно, кто-то начнет писать новые классы там, где это не нужно, просто чтобы избежать всех этих уродливых <> =D).

person epidemian    schedule 08.06.2012
comment
Спасибо. Значит, вы имеете в виду, что вывод типа не работает для параметров метода??? Всегда ли так? - person Sebastien Lorber; 08.06.2012
comment
@SebastienLorber Я не уверен. Кроме того, я думаю, что в Java 7 добавлены некоторые функции вывода типов, хотя первый пример по-прежнему не компилируется. Одна вещь, которая была добавлена, — вывод типов для вызовов универсальных конструкторов, но он работает так же, как и универсальные методы, то есть назначение к переменной использует определение типа, но использование выражения в качестве параметра напрямую не :S - person epidemian; 08.06.2012

В данном случае это способ сообщить методу reverseOrder, какой порядок должен применяться к объекту в зависимости от указанного вами типа. Компаратор должен получить конкретную информацию о том, как упорядочивать вещи.

person Logard    schedule 08.06.2012
comment
Извините, но тип в этом случае не имеет ничего общего с порядком: я могу указать тип, который хочу, под капотом всегда будет один и тот же объект времени выполнения, делающий одно и то же. Указанный тип просто помогает компилятору - person Sebastien Lorber; 08.06.2012