Java Guru 🤓
13.4K subscribers
938 photos
15 videos
793 links
Канал с вопросами и задачами с собеседований!

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

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

Мы на бирже: telega.in/channels/javatasks/card?r=lcDuijdm
Download Telegram
Каким будет результат компиляции и выполнения следующего кода?
Anonymous Quiz
52%
BABB
21%
ABBB
28%
Ошибка - StackOferflowError
👍143
Как инстанцировать экземпляр generic типа?

Внутри класса class Foo<T> на generic параметре T невозможно выполнить никакой оператор: нельзя взять его .class, нельзя применить его в instanceof. Также и вызов на нем оператора new приведет к ошибке.

Причина этих ограничений кроется в стирании типов. Дженерик параметры правильно воспринимать скорее как ограничения типов, чем как конкретные типы. Эти ограничения действуют для более строгих проверок на этапе компиляции. В рантайме же информация о конкретных переданных типах-параметрах стирается. А все эти операторы выполняются именно в рантайме.

Стандартный простой способ действия здесь – кроме значения типа T передавать еще и объект-дескриптор для этого типа, экземпляр класса Class<T>. Объект может быть создан из дескриптора рефлекшеном.

Но существует один хак, способный справиться со стиранием типов. Тип-параметр все-таки остается в одном месте в рантайме. Метод метакласса наследника определившего конкретный тип getGenericSuperclass() возвращает класс, которым параметризован родитель.
👍27🔥5❤‍🔥1
Каким будет результат компиляции и выполнения следующего кода?
👍9🌚5🔥1
👍10🥴5
Что означает ArrayStoreException?

Это исключение значит, что программа попыталась сохранить в массив значение неправильного типа. Такая попытка становится возможно из-за ковариантности массивов.

Ковариантность позволяет работать с массивом по типу массива родителей. Например, через приведение к Object[] можно попытаться положить любой объект в любой массив:

 Object x[] = new String[3];
x[0] = new Integer(0);

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

Ситуация похожа на проблему heap pollution в случае дженериков. Только для этого случая такая проблема возникает реже, потому что работает проверка этапа компиляции:

// Ошибка компиляции – дженерики инвариантны!
List<Object> x = new ArrayList<String>();
👍21
Каким будет результат компиляции и выполнения следующего кода?
👍7
Можно ли выбрасывать исключение generic-типа?

Короткий ответ – да. Как в большинстве каверзных вопросов про дженерики, ответ становится очевидным если подумать, во что сотрутся типы-параметры.

Чтобы объявить, что метод выбрасывает исключение обобщенного типа T, этот тип T должен быть объявлен расширяющим Throwable. Именно в Throwable в таком случае сотрется T при компиляции. Также в качестве типа-верхней границы можно использовать любого наследника Throwable:

class MyClass<T extends IOException> {
void foo() throws T {
// ...
}
}
👍17
Дженерики в исключениях – что можно, а что нельзя?

1. Можно выбрасывать исключение generic-типа.
Тип-параметр T может использоваться в throws, переменная типа T может использоваться в throw. Недавно мы уже говорили об этом.

2. Нельзя использовать дженерик в catch.
Множественные блоки catch должны идти без повторений, в определенном порядке – от специфичного класса к более базовому. Стирание типов-параметров в связи с этими правилами добавило бы путаницу, не неся особой пользы.

3. Нельзя параметризовать класс-исключение типами.
Если вы попытаетесь скомпилировать конструкцию вида class MyException<T> extends Throwable {}, то увидете ошибку generic class may not extend java.lang.Throwable.

4. Можно реализовывать исключением generic-интерфейс.
Исключение вполне может быть например Comparable или Iterable. Механизм обработки исключений работает на классах, никак не затрагивая интерфейсы.
👍15
Что выведет код?
👍15
👍23🌚5👌3
Как ограничить upcasting типа-параметра?

Задача: запретить этому методу принимать параметры разных типов:

<T> void pair(T a, T b) {}

То есть, нужно разрешить вызывать pair(Foo, Foo), но запретить pair(Foo, Bar).

Upcasting – приведение к типу-родителю. String → Object, Integer → Number.

Дело в том, что у любых двух классов есть общий предок: как минимум Object. Если вызвать этот метод с параметрами String и Boolean – согласно правилам вычисления типа-границы, параметр T будет стерт в Object.

Использовать super тоже не поможет: для этого нужно знать заранее, какой именно тип будет передаваться.

Фокус в том, что на этапе компиляции это невозможно. Объект любого типа всегда является объектом типа-родителя (отношение is a). Это фундаментальное правило ООП, которое невозможно нарушить. К тому же, подобный метод нарушал бы принцип подстановки Лисков.

Единственная возможность добиться желаемого поведения – с помощью getClass() сравнивать классы объектов в рантайме.
👍26🥰1
Каким будет результат компиляции и выполнения следующего кода?
👍12
Каким будет результат компиляции и выполнения следующего кода?
Anonymous Quiz
42%
Вывод в консоль - 1
5%
Вывод в консоль - 2
43%
Ошибка во время компиляции
10%
Ошибка во время исполнения
👍22🌭3
Когда нужно использовать raw types?

Сначала вспомним, что такое raw type. В Java так называют generic-типы без указания типа-параметра. Такая языковая конструкция валидна, но в большинстве случаев приводит к предупреждению компилятора.

Предупреждение связано с риском получения проблемы heap pollution. Ей мы уже посвящали публикации
ранее. Использование raw types никогда не оправдано – спецификация языка явно говорит: их поддержка остается только для обратной совместимости.

Есть всего три случая, когда использовать обобщенный тип без параметра правильно:
• Целевая версия Java < 5.0 (2002 год и ранее – вряд ли это ваш случай);
• В литерале класса. List<String>.class не сработает, нужно писать List.class;
• В операторе instanceof. Вместо instanceof Set<Integer> должно быть instanceof Set.
👍16🔥6
Каким будет результат компиляции и выполнения следующего кода?
👍11
Каким будет результат компиляции и выполнения следующего кода?
Anonymous Quiz
31%
Вывод в консоль - null
19%
Вывод в консоль - 1
30%
Вывод в консоль - 2
20%
Исключение NullPointerException
👍19🍾1
Как передать runtime информацию о generic-типе?

Когда вы проектируете API-метод библиотеки, иногда логика его реализации может зависеть от указанного клиентом типа. Особенно часто с этой задачей встречаются при разработке парсеров. Например, библиотека Jackson превращает JSON в объект заданного класса. На интервью этот вопрос можно встретить в виде практической задачи.

Первое, что приходит в голову для решения – дженерик-параметр. Такой подход не сработает, потому что тип будет стёрт во время компиляции, а логика будет происходить позже, во время выполнения.

Решение, которое сработает для многих случаев – объявление в методе аргумента типа Class<T>. Пользователь будет передавать в него значение Foo.class или fooInstance.getClass(). Проблемы с ним начинаются, когда становится нужно передать generic-тип. Синтаксис .class не поддерживает дженерики, а .getClass() от экземпляров List<String> и List<Integer> вернет один и тот же объект-описание сырого типа List.

На помощь приходит техника, описанная в предыдущей
публикации.

1. Объявляется generic класс-обертка над типом: TypeInformation<T>;. Наш метод будет принимать информацию о типе в виде экземпляра этой обертки.

2. В обертку добавляется конструктор с видимостью protected. Теперь можно создавать объекты только наследников, но не самого этого типа.

3. Пользователь будет передавать экземпляр анонимного наследника обертки: new TypeInformation<List<String>>() {}.

4. Внутри вызов getClass().getGenericSuperclass() вернет ParameterizedType. Это будет описание типа родителя анонима, то есть самой обертки. Из него с помощью getActualTypeArguments() можно достать рантайм-информацию о значении дженерика (о List<String>).
👍24🔥61
Что выведет на экран следующая программа?
👍13🔥3🤩1
Что выведет на экран следующая программа?
Anonymous Quiz
30%
true
31%
false
9%
4/2=true
30%
4/2=false
👍315🔥3🍌3🤩1