Java Guru 🤓
13.2K subscribers
893 photos
15 videos
761 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 ковариантно относительно типа результата и типов исключений.
👍16🤔3🎉31🔥1
Девять шаблонов проектирования ПО, которые вы должны знать!

Освойте их на бесплатном практическом уроке от OTUS и Андрея Полякова — старшего разработчика в Unlimint.

После вебинара вы сможете:
– понимать основные принципы и концепции, лежащие в основе шаблонов GRASP;
– оценивать и выбирать наиболее подходящие шаблоны для конкретных проектов;
– разрабатывать архитектуры, основанные на принципах GRASP, что позволит создавать более качественное и надежное ПО.

Занятие пройдёт 29 февраля в 20:00 мск в рамках курса «Архитектура и шаблоны проектирования». Доступна рассрочка на обучение!

➡️ Пройдите короткий тест прямо сейчас, чтобы посетить бесплатный урок и познакомиться с форматом обучения: пройти тест

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥1🍌1
Что такое bridge method?

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

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

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

Попытка написать такой же метод вручную приведет к ошибке компиляции.
👍21🔥4👏2❤‍🔥11
⁉️ Как стать разработчиком на Spring в 2024?

Узнайте на бесплатном практическом уроке от OTUS, где опытный эксперт приоткроет закулисье нашего курса и расскажет:

✔️ как грамотно выбрать итоговый проект, чтобы использовать его для портфолио и получить максимум практических навыков;
✔️ какие требования мы предъявляем к проектам студентов;
✔️ как проходит реализация проектов.

📢📢 Занятие пройдёт 28 февраля в 20:00 мск и будет приурочено к старту курса «Разработчик на Spring Framework».
Доступна рассрочка на обучение!

➡️ Пройдите короткий тест прямо сейчас, чтобы занять место на открытом уроке и получить запись: https://otus.pw/VSkT/

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥3
Что такое 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.
👍12🔥32🥰1
💪 Как Java-разработчику повысить квалификацию и увеличить доход в 2024?

Освоить JVM с профилированием и оптимизацией приложений в облаках на онлайн-курсе «Java Developer. Advanced» от OTUS.

Программа идеально подойдет для Java-разработчиков с опытом от 2-х лет, желающих углубить знания в устройстве JVM, принципах профилирования и оптимизации приложений в облачной инфраструктуре.

На курсе вас ждут:

▫️ живые вебинары с возможностью задать вопросы экспертам;
▫️много практики и сильные проекты для портфолио;
▫️поддержка профессионального комьюнити;
▫️ рассрочка на комфортных условиях.

🤝 Проверьте свои навыки пройдя тест и получите специальную цену: https://otus.pw/LiVg/

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👍2🥰21
Как работает вывод типов?

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

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

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

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

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

Типы выводимых параметров лямбда-выражения также можно не указывать. С Java 11 вместо типа указывается ключевое слово var. Такой синтаксис дает возможность добавлять параметру модификаторы и аннотации.
👍11🔥42❤‍🔥1
Как инстанцировать экземпляр generic типа?

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

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

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

Но существует один хак, способный справиться со стиранием типов. Тип-параметр все-таки остается в одном месте в рантайме. Метод метакласса наследника определившего конкретный тип getGenericSuperclass() возвращает класс, которым параметризован родитель.
👍1231
Бесплатный практический вебинар — Java: с чего начать карьеру в программировании?

Когда: 29 февраля в 19:00 по мск.

Расскажем за 2 часа, как освоить базовые навыки программирования на Java, найти удаленную работу и расти в профессии.

На практике познакомимся с синтаксисом языка и напишем программу, которую часто дают новичкам на техническом интервью.

🎁 Всем, кто зарегистрировался — Гайд «Как заговорить на сленге IT-специалистов», а каждому участнику — Карта компетенций Java-разработчика. 

Погрузитесь в основы Java и задайте вопросы опытному разработчику!
👍3
Что означает ArrayStoreException?

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

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

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

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

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

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

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

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

class MyClass<T extends IOException> {
void foo() throws T {
// ...
}
}
👍13🔥4
Начинаем рабочую неделю с подборки бесплатных обучающих вебинаров от «Умного города»!

27 февраля в 20:00 – Онлайн мастер-класс "Создание веб-дизайна баннера для сайта"
Аудитория: взрослые 18-35 лет
Ссылка на регистрацию: https://nordic-it-school.timepad.ru/event/2757299/

27 февраля в 17:00 – Онлайн мастер-класс для детей от 8 лет «Создание программы – переводчик в Scratch».
Ссылка на регистрацию: https://www.programmistik.ru/events/translator_scratch

28 февраля в 20:00 – Онлайн мастер-класс "Создание приложения по изучению английского языка на языке Kotlin для телефонов Android"
Аудитория: взрослые 18-35 лет
Ссылка на регистрацию: https://nordic-it-school.timepad.ru/event/2761604/

29 февраля в 17:00 – Онлайн мастер-класс для детей от 12 лет «Создаем онлайн-фотоальбом в конструкторе сайтов Tilda».
Ссылка на регистрацию: https://www.programmistik.ru/events/site_foto

Мастер-классы проводятся в трансляциях сообщества павильона «Умный город». Выбирайте и регистрируйтесь.

АНО «Цифровая трансформация» ИНН: 9709063543 Еrid:2VtzqxhnLA6
6👍2
Дженерики в исключениях – что можно, а что нельзя?

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. Механизм обработки исключений работает на классах, никак не затрагивая интерфейсы.
👍13🔥2
🤝 Нам кажется, что вы не готовы к нашему курсу Разработчик на Spring Framework

На курсе все максимально строго:

- Курс рассчитан на профессионалов с практическим опытом работы на Java.
- Мы будем 5 месяцев погружать вас в теорию и практику Spring Framework.
- Дадим 18 практических работ. ❗️Процесс review сдаваемой работы максимально приближен к тому, каким он мог быть на реальном проекте.
- Помимо этого, мы попросим вас сдать дипломную работу. Хотите узнать какие требования к ней (p.s. и чем она будет вам полезна)?

🤝 Готовы рассказать 28 февраля на бесплатном демо-уроке "Как будем выбирать проект?"

➡️ Чтобы записаться на демо-урок вам нужно еще и тест пройти.
Кстати, это отличный шанс проверить свои знания!

Пройти тест

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🎉21🥴1🌚1
Как ограничить 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() сравнивать классы объектов в рантайме.
👍12🔥2
Как делать профилирование с помощью VisualVM в Java?

Расскажет Александр Царев — Tech Lead в Сбере и кандидат технических наук.
Встречаемся на бесплатном практическом уроке от OTUS, где вы вместе с опытным экспертом:

- вспомните, что такое дамп потоков и как его получить;
- узнаете разницу между профилированием и семплированием и как их проводить с помощью инструмента VisualVM;
- разберете результаты семплирования, чтобы понять, как по ним находить узкие места в производительности.

📢📢 Занятие пройдёт 6 марта в 20:00 мск и будет приурочено к старту курса «Java Developer. Advanced».
Доступна рассрочка на обучение!

➡️ Пройдите короткий тест прямо сейчас, чтобы занять место на открытом уроке и получить запись: https://otus.pw/ZsER/

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Please open Telegram to view this post
VIEW IN TELEGRAM
👍52
Когда нужно использовать 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.
👍10
☕️Освойте ключевой навык разработчика на Java

Всего за пару часов на бесплатном практическом уроке «Дебаггинг приложений на Java» от OTUS.

На вебинаре:

Поговорим про выполнение Java-кода в самой популярной среде разработки;
Узнаем, как выполнять дебаггинг и как находить баги с помощью дебаггинга;
Ответим на все возникающие вопросы.

Встречаемся 5 марта в 20:00 мск в рамках курса «Специализация Java-разработчик». Доступна рассрочка на обучение!

Регистрируйтесь прямо сейчас, чтобы посетить бесплатный урок и получить запись: https://vk.cc/cv2ULt
👍5🔥3
Как передать 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>).
🔥15👍61
Отличается ли List<?> от List<? extends Object>?

Все классы без исключения наследуются от Object. Поэтому неограниченный wildcard <?> всегда подразумевает его в качестве верхней границы. Оба этих типа в рантайме сотрутся в List<Object>, функциональных отличий нет.

Не смотря на одинаковое поведение, существует одно синтаксическое различие. Неограниченный дженерик – reifiable тип. Это значит, что он представлен в рантайме. Такой тип можно использовать в операторе instanceof, тогда как синтаксическая конструкция x instanceof List<? extends Object> приведет к ошибке компиляции.
👍13😁2🔥1