Core-2: Jenerics Flashcards
Коваринтность, Контрваринтность, Инваринтность
Вариантность — перенос наследования исходных типов на производные от них типы. Под производными типами понимаются контейнеры, делегаты, обобщения, а не типы, связанные отношениями “предок-потомок”. Различными видами вариантности являются ковариантность, контравариантность и инвариантность.
Ковариантность — перенос наследования исходных типов на производные от них типы в прямом порядке.
Контравариантность — перенос наследования исходных типов на производные от них типы в обратном порядке.
Инвариантность — ситуация, когда наследование исходных типов не переносится на производные.
Что такое Дженерики?
Дженерики (или обобщения) - это параметризованные типы.
Параметризованные типы позволяют объявлять классы, интерфейсы и методы, где тип данных, которыми они оперируют, указан в виде параметра.
Дженерики –cвойства языка, позволяющие типизировать классы и методы.
Типизировать - явно указывать типы объектов, с которыми может работать данный метод, класс или содержать данная структура данных.
- Появились в версии 1.5
Для чего нужны дженерики? (2)
- Для строгой типизации и проверки на этапе компиляции:
Дженерики позволяют передавать тип объекта компилятору в формате<тип>
. Таким образом, компилятор может выполнить все необходимые действия по проверке типов во время компиляции, обеспечивая безопасность по стиранию и приведению типов во время выполнения:
Информация о типах доступна только на этапе компиляции и стирается в runtime, и в байт код попадет только информация о том, что в программе есть некий список List list вместо List<String> list, например.</String> - Для универсификации кода: Используя дженерики, можно создать единственный метод или класс, который будет автоматически работать с разными типами данных.
Что такое сырые типы (raw type)?
“Сырые типы — это дженерик класс или интерфейс без спецификации типа :
List list = new ArrayList(); class User<T>{} => { User user = new User; }
они использовались до появления дженериков.
Не указывая их, под капотом используется Object
Какие есть ограничения на использование Дженериков? (6)
1) работают только со ссылочными типами
2) Они инвариантны - наследование ограничено:
if (Foo extends Bar) -> List<Foo> !extends List<Bar>
3) типизированный Т объект нельзя объявить через new: Box<T> genericBox = new Box<T>();
- так нельзя
4) неприменимы к массивам, так как массивы ковариантны.
5) в обобщенном Т классе статические поля не могут быть static T, методы не могут возвращать Т. Зато стат. методы могут иметь параметры дженерики, причем этот метод необходимо дополнительно типизирвать (public class C <T> { static <T> Object act( T е) { return new Object();}}
)
6) Типизированные классы не могут наследоваться от Throwable.
Что означает конструкция public class Main <T extends Number>
?
Что класс работает с Number и наследниками, что дает возможность вызывать методы Number на T объекты. Такой возможности не было бы в случае class Main <T>
Что такое wildcard и какую проблему решает?
Языковая конструкция внутри даймонд-оператора.
Решает проблему наследования типов в дженериках. (коллекция <Интежер> не наследник коллекции <Намбер>)
Можем ли мы использовать ? для типизации параметра метода? (public static <?> void print(? item)
)
Нет, <?>
может только параметризовать объекты, надо делать так:public static <T> void print(T item)
Какое написание предпочтительнее: public static <E> void swap(List<E> list, int src, int des);
VS
public static void swap(List<?> list, int src, int des);
2е, так как если мы имеем неограниченный дженирик тип ( если подойдет вообще любая коллекция) - то следует использовать <?>
Так же если дженерик присутствует в объявлении метода лишь однажды, нам следуеет выбрать вайлдкард, даже если имеется верхнее или нижнее ограничиние.
Можем ли мы обратиться к методу public static <E> List<? extends Number> mergeWildcard(List<? extends E> listOne, List<? extends E> listTwo)
таким образом: List<Number> numbersMerged = .mergeWildcard(numbers1, numbers2);
?
Он не скомпилируется, так какList<Number> != List<? extends Number>
, ведь метод имеет право вернуть любой конкретный лист (<Integer>, <Double>, ...
).
Если мы изменим возвращаемое значение на List<E>
, то всё будет работать, так как теперь мы гарантируем, что у метода на входе и на выходе будут листы одного типа.
Таким образом, если метод возвращает дженеризированный объект, следует использовать типизированный дженерик, а не вайлдкард.
NB: Данный метод сработает если:List list = ... ; / List<? extends Number> = ...;
,
и потом мы сможем попытаться извлечь из него Number:
~~~
Number n = (Number) numbersMergeds.get(0);
~~~
Что означает тип List<? extends Number>
в аргументе метода?
Что придет коллекция, содержащая Number или наследников.
Что означает тип List<? Super Integer>
в аргументе метода?
Что придет коллекция, содержащая Integer или его предков.
Как понять List<?>
?
List<?> ==
List<? extend Object>
what is the difference between List<?> and List<T> in java?
- ? означает вообще любой тип и при чтении из этого листа возвращается Object
- из ? мы можем только читать, с Т можем делать всё.
объяснить public User<?> getUser()
метод возвращает юзера с неизвестным параметром. может использоваться когда тип не важен или он будет определяться в рантайме (например при доставании юзера из базы).
Что такое PECS?
Produser Extends - Ковариантность
Из List<? extends Number> list
мы можем читать только Number и его предков, но можем закастить в потомка:
Integer i = (Integer) list.get(0);
добавлять в <? extends Number>
т не typesafe, так в один лист могут залететь и Integer & Double, ведь они оба <? extends Number>
.
можем add(null);
_________________
Consumer Super: Контрвариантность
в List<? super Number> list
мы можем писать только Number и его наследников, так как конструкция гарантирует что лист содержит только Number и выше.
Можем прочитать из него Object и закастить, так как в рантайме конструкция станет List<Object> list
:
Integer n = (Integer) list.get(0);
Если мы хотим и писать и читать - wildcard использовать нельзя.
super применимо тольк к wildcard: <T super Number>
- ТАК НЕЛЬЗЯ.
Почему нельзя объявить static T поле в Class<T> ?</T>
static поле - общее для всех объектов класса, а входящее Т будет разное для каждого конкретного объекта.
Зачем параметризировать методы?
Если мы хотим расширить диапазон входных объектов, при условии, что функциональность и реализация должны быть общей для всех них
Надо ли в параметризованном классе явно параметризовать метод?
Если плейсхолдер класса и метода совпадают, то не надо, но
1) если в методе появляется дополнительный П.Х. - его надо указать
2)если метод static, то параметр класса и метода будут взаимонезависимы, даже если одинаково называются :
~~~
public class Test<T> {</T>
public <V> T avs(T t, V v ) { return (T) v; } public static <T> T method(List<? extends T> list) } ~~~
Что происходит при type erasure в конструкции <T extends Number>
?
Если в случае <T>
JVM стирает тип до Object, то в случае T extends Number тип сотрется Number
Как задать такой тип, который принимает только объекты наследующие N и имплементирующие 2 интерфейса?
<T extends N&Int1&Int2>
как происходит стирание типов в JVM?
LIst<Integer> al = new ArrayList<>(); JVM: LIst al = new ArrayList(); int i = al.get(0); JVM: int i = (Integer) al.get(0);
Что означает тип List<? extends Number>
в аргументе метода?
Что придет коллекция, содержащая Number или наследников.