Java Guru 🤓 – Telegram
Java Guru 🤓
13.1K subscribers
913 photos
15 videos
784 links
Канал с вопросами и задачами с собеседований!

По сотрудничеству и рекламе: @NadikaKir

Канал в перечне РКН: https://vk.cc/cJrSQZ

Мы на бирже: telega.in/channels/javatasks/card?r=lcDuijdm
Download Telegram
Что такое ковариантность и контравариантность?

Формально, ковариантность/контравариантность типов – это сохранение/обращение порядка наследования для производных типов. Проще говоря, когда у ковариантных сущностей типами-параметрами являются родитель и наследник, они сами становятся как бы родителем и наследником. Контравариантные наоборот, становятся наследником и родителем.

Легче всего осознать эти понятия на примерах:
🔘 Ковариантность: List<Integer> можно присвоить в переменную типа List<? extends Number> (как будто он наследник List<Number>).
🔘 Контравариантность: в качестве параметра метода List<Number>
#sort типа Comparator<? super Number> может быть передан Comparator<Object> (как будто он родитель Comparator<Number>)

Отношение типов «можно присвоить» – не совсем наследование, такие типы называются совместимыми (отношение «is a»).

Существует еще одно связанное понятие – инвариантность. Инвариантность – это отсутствие свойств ковариантности и контрвариантности. Дженерики без вайлдкардов инвариантны: List<Number> нельзя положить ни в переменную типа List<Double>, ни в List<Object>.

Массивы ковариантны: в переменную Object[] можно присвоить значение типа String[].

Переопределение методов начиная с Java 5 ковариантно относительно типа результата и типов исключений.
👍20🔥1
Какой будет результат?
👍11
Что такое bridge method?

В Java отсутствует ковариантность переопределенных методов по параметрам – их типы должны совпадать с типами параметров метода в родительском классе. Когда дженерик параметр конкретизируется в наследнике, методы с аргументами этого дженерик типа больше не совпадают в байткоде – в наследнике тип конкретный, а в родителе стертый до верхней границы.

Проблема решается простым и безопасным кастом. Компилятор генерирует новый метод, который совпадает по сигнатуре с родительским. В его теле параметр кастуется и вызов делегируется в пользовательский метод. Это и называется bridge методом.

Bridge method можно увидеть с помощью рефлекшна. Его имя совпадает с оригинальным методом, но параметр имеет тип, в который сотрется дженерик родителя. Этот метод будет помечен флагом synthetic, что значит, что он написан не программистом а компилятором.

Попытка написать такой же метод вручную приведет к ошибке компиляции.
👍16🔥41
Каким будет результат выполнения следующего кода?
👍17
Каким будет результат выполнения следующего кода?
Anonymous Quiz
36%
0
44%
-1
4%
Ничего из перечисленного
15%
Ошибка компиляции
👍37
Каким будет результат выполнения следующего кода?
👍11🌚211
Каким будет результат выполнения следующего кода?
Anonymous Quiz
17%
Value 1
56%
Value 3
6%
Value 2
20%
Ошибка компиляции
👍19🏆3
Каким будет результат выполнения следующего кода?
👍721
👍17🤨4🍾3
Каким будет результат выполнения следующего кода?
👍10
👍161🌚1
Что такое heap pollution?

Как было сказано ранее, массивы в Java ковариантны. А значит, можно обратиться к объекту типа String[] через переменную типа Object[], и положить туда например Integer. Такой код скомпилируется, но в момент записи произойдет ArrayStoreException.

Дженерики защищены инвариантностью. Если попытаться положить List<Object> в List<String>, эта же по сути ошибка произойдет уже на этапе компиляции.

Heap pollution – ситуация, когда эта защита не срабатывает, и переменная параметризованного типа хранит в себе объект, параметризованный другим типом. Простейший пример:

List<String> strings = (List) new ArrayList<Integer>();

Документация гарантирует, что при компиляции всего кода целиком, heap pollution не может возникнуть без варнинга этапа компиляции.
Heap pollution может произойти в двух случаях: при использовании массивов дженериков и при смешивании параметризованных и raw-типов.

Raw types – это параметризованные типы без указания параметра. Пример с raw types, приводящий к heap pollution, уже был описан выше:

List<String> strings = (List) new ArrayList<Integer>();

Использовать raw types не надо вообще, причины подробно изложены в главе 26 Effective Java. Если информация о дженериках не нужна, используется символ wildcard (<?>).

Компилятор не даст создать массив параметризованного типа, это приведет к ошибке generic array creation. Картинка выше иллюстрирует, к чему это могло бы привести.

Параметризованный тип varargs-аргумента метода вызывает ту же проблему, т.к. varargs – не что иное как параметр-массив. Вот почему он так же приводит к предупреждению компилятора «possible heap pollution». Если вы уверены что риска нет, с Java 7 это предупреждение заглушается аннотацией
@SafeVarargs.
👍245🔥3
Еще один классный канал для изучения Java
@Java_per_month
👍4
Каким будет результат компиляции и выполнения следующего кода?
👍15
Каким будет результат компиляции и выполнения следующего кода?
Anonymous Quiz
18%
C5
18%
C
26%
ABC5
29%
ABC
9%
AB5
👍18🤔17🔥2🤯1
Каким будет результат компиляции и выполнения следующего кода?
👍9
Каким будет результат компиляции и выполнения следующего кода?
Anonymous Quiz
27%
true
60%
false
12%
Ошибка при исполнении.
👍15🤔2
Как работает вывод типов?

Для
начала разберемся, что такое вывод типов. Type inference – это способность компилятора догадаться, какой тип нужно подставить, и сделать это за вас. На обычном интервью никто не спросит детали алгоритма вывода типов, достаточно будет сказать, что вывод происходит статически, только на основании типов аргументов и ожидаемого типа результата. По сути, вопрос заключается не в «как работает?», а «что это и когда возникает?».

Первое, что многим приходит в голову при фразе «вывод типов» – diamond operator <>. Он появился в Java с версии 7. Его применяют к конструкторам дженерик классов, чтобы отличать требование автоматического вывода типа от raw type.

С Java 9 diamond operator заработал и для анонимных классов.

Для дженерик методов можно указывать параметр явно, но diamond синтаксически недопустим – вывод и так сработает по умолчанию.

В Java 10 для вывода типа локальной переменной добавлено ключевое слово var. Работает это так же, как в большинстве современных языков – ключевое слово ставится вместо типа при объявлении.

Типы выводимых параметров лямбда-выражения также можно не указывать. С Java 11 вместо типа указывается ключевое слово var. Такой синтаксис дает возможность добавлять параметру модификаторы и аннотации.
👍15🔥3
Каким будет результат компиляции и выполнения следующего кода?
👍131