Руководство по родовым типам в Java (Java Generics) — описание и примеры

Genrics* были добавлены в Java 5 и сейчас являются неотъемлемой частью Java Core. Если вы знакомы с Java Collections версии 5 или выше, то я уверен, что вы использовали generics в своих программах.

*Дженерики/Родовые типы — далее в статье я буду использовать название Generics или дженерики как наиболее используемые, хотя правильный перевод все-таки Родовые типы

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

Кратко о Generics

Начиная с Java 5 весь Collection Framework был переписан с нуля уже с использованием Generics. Давайте посмотрим как дженерики помогают безопасно использовать классы коллекций.

Пример без дженериков

Код выше компилируется нормально, но бросает ClassCastException, потому что мы пытаемся привести объект в списке к типу String в то время как один из элементов списка является Integer. Начиная с Java 5 код выше будет переписан так как показано ниже:

Пример с дженериками:

Обратите внимание, что на момент создания списка, мы указали, что типом элементов в списке будет строка (String). Так что, если мы пытаемся добавить любой другой тип объекта в список, программа выдаст ошибку компиляции. Также обратите внимание, что во время обхода списка в цикле for мы не используем приведение типов, а это значит, что нам не нужно беспокоиться о ClassCastException.

Использование Generics в интерфейсах и классах

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

Чтобы понять это, давайте создадим простой класс.

Без generics:

Обратите внимание, что при использовании этого класса, мы должны приводить типы объектов, что может привести к ClassCastException. Теперь мы будем использовать дженерики — перепишем тот же класс но уже с generics.

С generics:

Обратите внимание на main() метод класса GenericsType. Нам не нужно беспокоиться о приведении типов, поэтому нам не страшен ClassCastException. Если мы не пишем тип в момент создания экземпляра класса, то компилятор предупредит вас следующим сообщением "GenericsType is a raw type. References to generic type GenericsType<T> should be parameterized". Давайте расшифруем это сообщение. Когда мы не пишем тип в момент создания экземпляра класса, то тип автоматически становится Object и, следовательно, это позволяет использовать как объекты String, так и другие объекты Integer. Это аннулирует преимущества дженериков в Java и заставит использовать приведение типов, то есть это будет класс OldSchool (код смотри выше).

(Хотя мы можем использовать аннотацию @SuppressWarnings("rawtypes"), чтобы подавить предупреждение компилятора, но это уже тема другой статьи)

Также следует отметить, что мы использовали автоупаковку в Java (Java Autoboxing).

Generics в интерфейсах

Интерфейс Comparable является отличным примером использования Generics в интерфейсах.

Мы можем брать этот код для примера и использовать дженерики в наших интерфейсах и классах. Мы также можем использовать несколько параметров, например, интерфейс Map. Вот пример: new HashMap<String, List<String>>() —  круто, правда? 🙂

Generics: Соглашение об именовании

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

  • E — элемент (широко используется в Java Collections Framework, например, ArrayList, Set и т.д.)
  • К — ключ (используется в Map)
  • Т — Тип
  • В — значение (используется в Map)
  • S, U, V и т.д. — 2й, 3й, 4й… тип

Дженерики в методах и конструкторах

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

Давайте рассмотрим это сразу на примере класса с дженериками в методах.

Дженерики и наследование

Мы знаем, что наследование Java позволяет назначить переменную A другой переменной В, если А является подклассом В. Таким образом, мы могли бы предположить, что любой тип дженерика А может быть назначен дженерику типа В, но это не так. Давайте посмотрим пример простой программы.

Следите за обновлениями на Javadevblog.com

 

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *