Дженерики Flashcards
Что такое дженерики?
Дженерики (обобщения) — это особые средства языка Java для использования обобщенных типов и методов.
Это вид параметрического полиморфизма.
Обобщенные типы и методы отличаются от обычных тем, что имеют типизированные параметры.
Существует 4 различных способа применения:
Типовой класс: Класс называется дженериком, если он объявляет одну или несколько переменных типа.
Интерфейс: то же с интерфейсом
Метод: отличаются тем что информация о области действия или типе находится только внутри метода
Конструктор: дженерики могут указываться в атрибутах
Преимущества дженериков в Java
Повторное использование кода.Generics - это технический термин, обозначающий набор свойств языка позволяющих определять и использовать обобщенные типы и методы. Обобщенные типы или методы отличаются от обычных тем, что имеют типизированные параметры.
Примером использования обобщенных типов может служить Java Collection Framework. Так, класс LinkedList - типичный обобщенный тип. Он содержит параметр E, который представляет тип элементов, которые будут храниться в коллекции. Создание объектов обобщенных типов происходит посредством замены параметризированных типов реальными типами данных. Вместо того, чтобы просто использовать LinkedList, ничего не говоря о типе элемента в списке, предлагается использовать точное указание типа LinkedList, LinkedList и т.п.
Для чего нужны дженерики?
- позволяет писать код / использовать библиотечные методы, которые являются типобезопасными, т. е. List гарантированно является списком строк.
- компилятор может проверить во время компиляции, что ограничения типа не нарушаются во время выполнения. Поскольку программа компилируется без предупреждений, мы можем с уверенностью утверждать, что она не будет вызывать ClassCastException во время выполнения.
- позволяет писать код, который применим ко многим типам с одинаковым базовым поведением
- основным преимуществом, как указывает Митчел, является сильная типизация без необходимости определения нескольких классов.
- отсутствие необходимости в типизации является одним из самых больших преимуществ Java generics, так как он будет выполнять проверку типов во время компиляции. Это уменьшит возможность ClassCastExceptions, которые могут быть брошены во время выполнения и могут привести к более надежному коду.
Что такое сырые типы (raw type)?
Сырой тип (raw type) — это имя обобщённого класса или интерфейса без аргументов типа (type arguments).
Например, параметризованный тип создаётся так:
Lair goblinLair = new Lair<>();
Если убрать аргументы типа, то будет создан сырой тип:
Lair lair = new Lair();
Поэтому Lair — это сырой тип обобщённого типа Lair . Однако необобщённый класс или интерфейс НЕ являются сырыми типами.
Для совместимости со старым кодом допустимо присваивать параметризованный тип своему собственному сырому типу:
Lair goblinLair = new Lair<>();
Lair lair = goblinLair; // OK
Но если вы попытаетесь присвоить параметризованному типу сырой тип, то будет предупреждение (warning):
Lair lair = new Lair();
Lair goblinLair = lair; // warning
Сообщения об ошибках “unchecked”
Как упоминалось выше, при использовании сырого типа вы можете столкнуться с предупреждениями вида: Термин “unchecked” означает непроверенные, то есть компилятор не имеет достаточного количества информации для обеспечения безопасности типов.
Что такое вайлдкарды?
Проблема: Между объектом и коллекцией объектов есть важное различие. Если класс B является наследником класса А, то Collection<b> при этом — не наследник Collection<a>.
List strings = new ArrayList();
// ошибка компиляции!
List objects = strings;
Таким образом, мы лишились гарантии того, что в нашей коллекции находятся только указанные в дженерике объекты String. То есть, мы потеряли главное преимущество дженериков — типобезопасность.</a></b>
Первый из нескольких типов wildcard — “extends” (другое название — Upper Bounded Wildcards). Это значит, что метод принимает на вход коллекцию объектов класса Animal либо объектов любого класса-наследника Animal (? extends Animal)
“super” (другое название — Lower Bounded Wildcards). Конструкция говорит компилятору, что метод iterateAnimals() может принимать на вход коллекцию объектов класса Cat либо любого другого класса-предка Cat.
</a></b>
Как Generics работает в Java? Что такое стирание типа?
дженерики появились только в версии языка Java 5. К моменту ее выхода программисты успели написать кучу кода с использованием Raw Types, и чтобы он не перестал работать, возможность создания и работы Raw Types в Java сохранилась.
Однако эта проблема оказалась гораздо обширнее.
Java-код, как ты знаешь, преобразуется в специальный байт-код, который потом выполняется виртуальной машиной Java.
И если бы в процессе перевода мы помещали в байт-код информацию о типах-параметрах, это сломало бы весь ранее написанный код, ведь до Java 5 никаких типов-параметров не существовало!
При работе с дженериками есть одна очень важная особенность, о которой необходимо помнить. Она называется “стирание типов” (type erasure).
Ее суть заключается в том, что внутри класса не хранится никакой информации о его типе-параметре.
Эта информация доступна только на этапе компиляции и стирается (становится недоступной) в runtime.
Если ты попытаешься положить объект не того типа в свой List, компилятор выдаст ошибку. Этого как раз и добивались создатели языка, создавая дженерики — проверки на этапе компиляции.
Но когда весь написанный тобой Java-код превратится в байт-код, в нем не будет информации о типах-параметрах.
В отличие от дженериков, массивы знают и могут использовать информацию о своем типе данных во время выполнения программы.Из-за того, что между массивами и дженериками есть такая большая разница, у них могут возникнуть проблемы с совместимостью.
Прежде всего, ты не можешь создать массив объектов-дженериков или даже просто типизированный массив.
Расскажите про принцип PECS
PECS — Producer Extends, Consumer Super. Его суть:
Коллекции с wildcards и ключевым словом extends — это producers (производители, генераторы), они лишь предоставляют данные.
Коллекции с wildcards и ключевым словом super — это consumers (потребители), они принимают данные, но не отдают их.
Чиать: Объекты SomeType и всех его супертипов
Писать: Только null
Читать: Объекты типа Object Писать: Объекты типа SomeType и всех его подтипов
Дженерики (инвариантность, ковариантность и контрвариантность,
Формально, ковариантность/контравариантность типов – это сохранение/обращение порядка наследования для производных типов. Проще говоря, когда у ковариантных сущностей типами-параметрами являются родитель и наследник, они сами становятся как бы родителем и наследником. Контравариантные наоборот, становятся наследником и родителем.
Легче всего осознать эти понятия на примерах:
🔘 Ковариантность: List можно присвоить в переменную типа List (как будто он наследник List).
🔘 Контравариантность: в качестве параметра метода List#sort типа Comparator может быть передан Comparator (как будто он родитель Comparator)
Инвариантность – это отсутствие свойств ковариантности и контрвариантности. Дженерики без вайлдкардов инвариантны: List нельзя положить ни в переменную типа List, ни в List.
Массивы ковариантны: в переменную Object[] можно присвоить значение типа String[].
Множественные ограничения
Также можно установить сразу несколько ограничений. Например, пусть класс Transaction может работать только с объектами, которые одновременно реализуют интерфейс Accountable и являются наследниками класса Person:
class Person{} interface Accountable{}
class Transaction{}