Руководство по родовым типам в 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

 

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

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