Узнайте на бесплатном практическом уроке от OTUS, где опытный эксперт приоткроет закулисье нашего курса и расскажет:
✔️ как грамотно выбрать итоговый проект, чтобы использовать его для портфолио и получить максимум практических навыков;
✔️ какие требования мы предъявляем к проектам студентов;
✔️ как проходит реализация проектов.
Доступна рассрочка на обучение!
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ruPlease open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥3
Что такое heap pollution?
Как было сказано ранее, массивы в Java ковариантны. А значит, можно обратиться к объекту типа String[] через переменную типа Object[], и положить туда например Integer. Такой код скомпилируется, но в момент записи произойдет ArrayStoreException.
Дженерики защищены инвариантностью. Если попытаться положить List<Object> в List<String>, эта же по сути ошибка произойдет уже на этапе компиляции.
Heap pollution – ситуация, когда эта защита не срабатывает, и переменная параметризованного типа хранит в себе объект, параметризованный другим типом. Простейший пример:
Heap pollution может произойти в двух случаях: при использовании массивов дженериков и при смешивании параметризованных и raw-типов.
Raw types – это параметризованные типы без указания параметра. Пример с raw types, приводящий к heap pollution, уже был описан выше:
Компилятор не даст создать массив параметризованного типа, это приведет к ошибке generic array creation. Картинка выше иллюстрирует, к чему это могло бы привести.
Параметризованный тип varargs-аргумента метода вызывает ту же проблему, т.к. varargs – не что иное как параметр-массив. Вот почему он так же приводит к предупреждению компилятора «possible heap pollution». Если вы уверены что риска нет, с Java 7 это предупреждение заглушается аннотацией @SafeVarargs.
Как было сказано ранее, массивы в 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🔥3❤2🥰1
Освоить JVM с профилированием и оптимизацией приложений в облаках на онлайн-курсе «Java Developer. Advanced» от OTUS.
Программа идеально подойдет для Java-разработчиков с опытом от 2-х лет, желающих углубить знания в устройстве JVM, принципах профилирования и оптимизации приложений в облачной инфраструктуре.
На курсе вас ждут:
▫️ живые вебинары с возможностью задать вопросы экспертам;
▫️много практики и сильные проекты для портфолио;
▫️поддержка профессионального комьюнити;
▫️ рассрочка на комфортных условиях.
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👍2🥰2❤1
Как работает вывод типов?
Для начала разберемся, что такое вывод типов. Type inference – это способность компилятора догадаться, какой тип нужно подставить, и сделать это за вас. На обычном интервью никто не спросит детали алгоритма вывода типов, достаточно будет сказать, что вывод происходит статически, только на основании типов аргументов и ожидаемого типа результата. По сути, вопрос заключается не в «как работает?», а «что это и когда возникает?».
Первое, что многим приходит в голову при фразе «вывод типов» – diamond operator <>. Он появился в Java с версии 7. Его применяют к конструкторам дженерик классов, чтобы отличать требование автоматического вывода типа от raw type.
С Java 9 diamond operator заработал и для анонимных классов.
Для дженерик методов можно указывать параметр явно, но diamond синтаксически недопустим – вывод и так сработает по умолчанию.
В Java 10 для вывода типа локальной переменной добавлено ключевое слово var. Работает это так же, как в большинстве современных языков – ключевое слово ставится вместо типа при объявлении.
Типы выводимых параметров лямбда-выражения также можно не указывать. С Java 11 вместо типа указывается ключевое слово var. Такой синтаксис дает возможность добавлять параметру модификаторы и аннотации.
Для начала разберемся, что такое вывод типов. Type inference – это способность компилятора догадаться, какой тип нужно подставить, и сделать это за вас. На обычном интервью никто не спросит детали алгоритма вывода типов, достаточно будет сказать, что вывод происходит статически, только на основании типов аргументов и ожидаемого типа результата. По сути, вопрос заключается не в «как работает?», а «что это и когда возникает?».
Первое, что многим приходит в голову при фразе «вывод типов» – diamond operator <>. Он появился в Java с версии 7. Его применяют к конструкторам дженерик классов, чтобы отличать требование автоматического вывода типа от raw type.
С Java 9 diamond operator заработал и для анонимных классов.
Для дженерик методов можно указывать параметр явно, но diamond синтаксически недопустим – вывод и так сработает по умолчанию.
В Java 10 для вывода типа локальной переменной добавлено ключевое слово var. Работает это так же, как в большинстве современных языков – ключевое слово ставится вместо типа при объявлении.
Типы выводимых параметров лямбда-выражения также можно не указывать. С Java 11 вместо типа указывается ключевое слово var. Такой синтаксис дает возможность добавлять параметру модификаторы и аннотации.
👍11🔥4❤2❤🔥1
Как инстанцировать экземпляр generic типа?
Внутри класса class Foo<T> на generic параметре T невозможно выполнить никакой оператор: нельзя взять его .class, нельзя применить его в instanceof. Также и вызов на нем оператора new приведет к ошибке.
Причина этих ограничений кроется в стирании типов. Дженерик параметры правильно воспринимать скорее как ограничения типов, чем как конкретные типы. Эти ограничения действуют для более строгих проверок на этапе компиляции. В рантайме же информация о конкретных переданных типах-параметрах стирается. А все эти операторы выполняются именно в рантайме.
Стандартный простой способ действия здесь – кроме значения типа T передавать еще и объект-дескриптор для этого типа, экземпляр класса Class<T>. Объект может быть создан из дескриптора рефлекшеном.
Но существует один хак, способный справиться со стиранием типов. Тип-параметр все-таки остается в одном месте в рантайме. Метод метакласса наследника определившего конкретный тип getGenericSuperclass() возвращает класс, которым параметризован родитель.
Внутри класса class Foo<T> на generic параметре T невозможно выполнить никакой оператор: нельзя взять его .class, нельзя применить его в instanceof. Также и вызов на нем оператора new приведет к ошибке.
Причина этих ограничений кроется в стирании типов. Дженерик параметры правильно воспринимать скорее как ограничения типов, чем как конкретные типы. Эти ограничения действуют для более строгих проверок на этапе компиляции. В рантайме же информация о конкретных переданных типах-параметрах стирается. А все эти операторы выполняются именно в рантайме.
Стандартный простой способ действия здесь – кроме значения типа T передавать еще и объект-дескриптор для этого типа, экземпляр класса Class<T>. Объект может быть создан из дескриптора рефлекшеном.
Но существует один хак, способный справиться со стиранием типов. Тип-параметр все-таки остается в одном месте в рантайме. Метод метакласса наследника определившего конкретный тип getGenericSuperclass() возвращает класс, которым параметризован родитель.
👍12❤3☃1
Бесплатный практический вебинар — Java: с чего начать карьеру в программировании?
⏰ Когда: 29 февраля в 19:00 по мск.
Расскажем за 2 часа, как освоить базовые навыки программирования на Java, найти удаленную работу и расти в профессии.
На практике познакомимся с синтаксисом языка и напишем программу, которую часто дают новичкам на техническом интервью.
🎁 Всем, кто зарегистрировался — Гайд «Как заговорить на сленге IT-специалистов», а каждому участнику — Карта компетенций Java-разработчика.
Погрузитесь в основы Java и задайте вопросы опытному разработчику!
⏰ Когда: 29 февраля в 19:00 по мск.
Расскажем за 2 часа, как освоить базовые навыки программирования на Java, найти удаленную работу и расти в профессии.
На практике познакомимся с синтаксисом языка и напишем программу, которую часто дают новичкам на техническом интервью.
🎁 Всем, кто зарегистрировался — Гайд «Как заговорить на сленге IT-специалистов», а каждому участнику — Карта компетенций Java-разработчика.
Погрузитесь в основы Java и задайте вопросы опытному разработчику!
👍3
Что означает ArrayStoreException?
Это исключение значит, что программа попыталась сохранить в массив значение неправильного типа. Такая попытка становится возможно из-за ковариантности массивов.
Ковариантность позволяет работать с массивом по типу массива родителей. Например, через приведение к Object[] можно попытаться положить любой объект в любой массив:
Ситуация похожа на проблему heap pollution в случае дженериков. Только для этого случая такая проблема возникает реже, потому что работает проверка этапа компиляции:
// Ошибка компиляции – дженерики инвариантны!
Это исключение значит, что программа попыталась сохранить в массив значение неправильного типа. Такая попытка становится возможно из-за ковариантности массивов.
Ковариантность позволяет работать с массивом по типу массива родителей. Например, через приведение к Object[] можно попытаться положить любой объект в любой массив:
Object x[] = new String[3];Компилятор гарантирует, что когда вы берете элемент из массива, он будет представителем типа элементов самого этого массива. Не важно какого типа переменная его хранит. Именно для обеспечения этой гарантии работает проверка типа времени выполнения, которая и выбрасывает ArrayStoreException.
x[0] = new Integer(0);
Ситуация похожа на проблему heap pollution в случае дженериков. Только для этого случая такая проблема возникает реже, потому что работает проверка этапа компиляции:
// Ошибка компиляции – дженерики инвариантны!
List<Object> x = new ArrayList<String>();👍13❤1
Можно ли выбрасывать исключение generic-типа?
Короткий ответ – да. Как в большинстве каверзных вопросов про дженерики, ответ становится очевидным если подумать, во что сотрутся типы-параметры.
Чтобы объявить, что метод выбрасывает исключение обобщенного типа T, этот тип T должен быть объявлен расширяющим Throwable. Именно в Throwable в таком случае сотрется T при компиляции. Также в качестве типа-верхней границы можно использовать любого наследника Throwable:
Короткий ответ – да. Как в большинстве каверзных вопросов про дженерики, ответ становится очевидным если подумать, во что сотрутся типы-параметры.
Чтобы объявить, что метод выбрасывает исключение обобщенного типа 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
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. Механизм обработки исключений работает на классах, никак не затрагивая интерфейсы.
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
На курсе все максимально строго:
- Курс рассчитан на профессионалов с практическим опытом работы на Java.
- Мы будем 5 месяцев погружать вас в теорию и практику Spring Framework.
- Дадим 18 практических работ. ❗️Процесс review сдаваемой работы максимально приближен к тому, каким он мог быть на реальном проекте.
- Помимо этого, мы попросим вас сдать дипломную работу. Хотите узнать какие требования к ней (p.s. и чем она будет вам полезна)?
Кстати, это отличный шанс проверить свои знания!
Пройти тест
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ruPlease open Telegram to view this post
VIEW IN TELEGRAM
👍5🎉2❤1🥴1🌚1
Как ограничить upcasting типа-параметра?
Задача: запретить этому методу принимать параметры разных типов:
Upcasting – приведение к типу-родителю. String → Object, Integer → Number.
Дело в том, что у любых двух классов есть общий предок: как минимум Object. Если вызвать этот метод с параметрами String и Boolean – согласно правилам вычисления типа-границы, параметр T будет стерт в Object.
Использовать super тоже не поможет: для этого нужно знать заранее, какой именно тип будет передаваться.
Фокус в том, что на этапе компиляции это невозможно. Объект любого типа всегда является объектом типа-родителя (отношение is a). Это фундаментальное правило ООП, которое невозможно нарушить. К тому же, подобный метод нарушал бы принцип подстановки Лисков.
Единственная возможность добиться желаемого поведения – с помощью getClass() сравнивать классы объектов в рантайме.
Задача: запретить этому методу принимать параметры разных типов:
<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/
Расскажет Александр Царев — Tech Lead в Сбере и кандидат технических наук.
Встречаемся на бесплатном практическом уроке от OTUS, где вы вместе с опытным экспертом:
- вспомните, что такое дамп потоков и как его получить;
- узнаете разницу между профилированием и семплированием и как их проводить с помощью инструмента VisualVM;
- разберете результаты семплирования, чтобы понять, как по ним находить узкие места в производительности.
Доступна рассрочка на обучение!
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤2
Когда нужно использовать 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.
Сначала вспомним, что такое 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
Всего за пару часов на бесплатном практическом уроке «Дебаггинг приложений на 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>).
Когда вы проектируете 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👍6⚡1
Отличается ли List<?> от List<? extends Object>?
Все классы без исключения наследуются от Object. Поэтому неограниченный wildcard <?> всегда подразумевает его в качестве верхней границы. Оба этих типа в рантайме сотрутся в List<Object>, функциональных отличий нет.
Не смотря на одинаковое поведение, существует одно синтаксическое различие. Неограниченный дженерик – reifiable тип. Это значит, что он представлен в рантайме. Такой тип можно использовать в операторе instanceof, тогда как синтаксическая конструкция x instanceof List<? extends Object> приведет к ошибке компиляции.
Все классы без исключения наследуются от Object. Поэтому неограниченный wildcard <?> всегда подразумевает его в качестве верхней границы. Оба этих типа в рантайме сотрутся в List<Object>, функциональных отличий нет.
Не смотря на одинаковое поведение, существует одно синтаксическое различие. Неограниченный дженерик – reifiable тип. Это значит, что он представлен в рантайме. Такой тип можно использовать в операторе instanceof, тогда как синтаксическая конструкция x instanceof List<? extends Object> приведет к ошибке компиляции.
👍13😁2🔥1
Зачем нужна Java Serialization? Как сделать класс сериализуемым?
Сериализация – это сохранение типа и состояния объекта в бинарном виде для последующего сохранения/передачи и десериализации. Для сериализации используется интерфейс Serializable (Externalizable), и цель записи/источник чтения ObjectInputStream/ObjectOutputStream (ObjectInput/ObjectOutput).
Класс сериализуем, если:
🔘 Реализует маркерный интерфейс Serializable;
🔘 Все поля сериализуемые или помечены модификатором transient (иначе рантайм выбросит NotSerializableException).
Протокол стандартной сериализации описан в документации.
Сериализационная форма может, и в некоторых случаях для корректной работы должна быть кастомизирована. Правильная форма содержит только логическое представление данных, без деталей о физической реализации.
Для описания полей сериализационной формы в javadoc-документации используется тэг @serial. Для документации генерирующего нестандартную сериализационную форму метода используется @serialData. Эти тэги имеют смысл и для приватных членов, так как эффективно такие члены – часть публичного API.
Нестатические внутренние классы не должны быть сериализуемыми. Статические поля как поля класса а не инстанса несериализуемы.
Сериализация – это сохранение типа и состояния объекта в бинарном виде для последующего сохранения/передачи и десериализации. Для сериализации используется интерфейс Serializable (Externalizable), и цель записи/источник чтения ObjectInputStream/ObjectOutputStream (ObjectInput/ObjectOutput).
Класс сериализуем, если:
🔘 Реализует маркерный интерфейс Serializable;
🔘 Все поля сериализуемые или помечены модификатором transient (иначе рантайм выбросит NotSerializableException).
Протокол стандартной сериализации описан в документации.
Сериализационная форма может, и в некоторых случаях для корректной работы должна быть кастомизирована. Правильная форма содержит только логическое представление данных, без деталей о физической реализации.
Для описания полей сериализационной формы в javadoc-документации используется тэг @serial. Для документации генерирующего нестандартную сериализационную форму метода используется @serialData. Эти тэги имеют смысл и для приватных членов, так как эффективно такие члены – часть публичного API.
Нестатические внутренние классы не должны быть сериализуемыми. Статические поля как поля класса а не инстанса несериализуемы.
👍6🔥2
Зачем используется Serial Version UID? Что если не определить его?
Сериализуемый класс явно или неявно, но всегда имеет serialVersionUID. Это число типа long, которое представляет собой «версию» сериализационной формы класса. Если при сериализации/десериализации значения serialVersionUID не совпадают – будет выброшено InvalidClassException.
Для совпадающих версий работает мощная поддержка эволюции класса – совместимые изменения, такие как добавление или удаление полей, не приводят к InvalidClassException.
Неявное значение вычисляется автоматически в рантайме, и включает в себя информацию о имени типа, списке родителей и полей (с точностью до коллизии). По смыслу это похоже на хэш-сумму класса. Соответственно, при любом изменении класса значение изменится, и поддержка эволюции окажется бесполезной.
Всегда лучше явно указывать любое значение serialVersionUID, и изменять только в тех редких случаях, когда требуется сломать совместимость с предыдущими версиями. Стандартная утилита JDK serialver умеет «угадывать» авто-генерированное значение. Она используется чтобы зафиксировать значение для включения поддержки эволюции созданного ранее класса.
Явное значение устанавливается в переменную static final long serialVersionUID.
Сериализуемый класс явно или неявно, но всегда имеет serialVersionUID. Это число типа long, которое представляет собой «версию» сериализационной формы класса. Если при сериализации/десериализации значения serialVersionUID не совпадают – будет выброшено InvalidClassException.
Для совпадающих версий работает мощная поддержка эволюции класса – совместимые изменения, такие как добавление или удаление полей, не приводят к InvalidClassException.
Неявное значение вычисляется автоматически в рантайме, и включает в себя информацию о имени типа, списке родителей и полей (с точностью до коллизии). По смыслу это похоже на хэш-сумму класса. Соответственно, при любом изменении класса значение изменится, и поддержка эволюции окажется бесполезной.
Всегда лучше явно указывать любое значение serialVersionUID, и изменять только в тех редких случаях, когда требуется сломать совместимость с предыдущими версиями. Стандартная утилита JDK serialver умеет «угадывать» авто-генерированное значение. Она используется чтобы зафиксировать значение для включения поддержки эволюции созданного ранее класса.
Явное значение устанавливается в переменную static final long serialVersionUID.
👍10🔥3
😨Вы видели этот хардовый тест для Java QA Engineer'ов (не для новичков)? Его проходят всего 30%
👉 Пройдите тест из 20 вопросов онлайн-курса «Java QA Engineer. Professional» и узнайте, осилите ли вы обучение в OTUS.
🟢 Пройти тест: https://otus.pw/sPZA/
💣Все, кто успешно пройдет тест, получит доступ к записям уроков курса для знакомства с форматом обучения и спец.цену на курс.
💻 За 4 месяца обучения на курсе вы освоите:
— популярные инструменты автоматизации
— полный спектр технологий тестирования на Java
— ключевые инструменты автоматизации UI- и API-тестирования
— навыки работы с RestAssured и JsonSchemaValidator
— навыки работы с Appium
— многопоточное тестирование с помощью Selenoid
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963. erid: LjN8JvwWN
👉 Пройдите тест из 20 вопросов онлайн-курса «Java QA Engineer. Professional» и узнайте, осилите ли вы обучение в OTUS.
🟢 Пройти тест: https://otus.pw/sPZA/
💣Все, кто успешно пройдет тест, получит доступ к записям уроков курса для знакомства с форматом обучения и спец.цену на курс.
💻 За 4 месяца обучения на курсе вы освоите:
— популярные инструменты автоматизации
— полный спектр технологий тестирования на Java
— ключевые инструменты автоматизации UI- и API-тестирования
— навыки работы с RestAssured и JsonSchemaValidator
— навыки работы с Appium
— многопоточное тестирование с помощью Selenoid
Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963. erid: LjN8JvwWN
👍5🔥1
Какими методами настраивается сериализация?
Интерфейс Serializable пуст, но есть ряд методов, добавив которые в сериализуемый класс можно добиться изменения этапов процесса сериализации и десериализации.
readObjectNoData() используется для инициализации класса-родителя, который при сериализации оригинального объекта еще не был родителем. Подробное объяснение.
readObject(ObjectInputStream s) переопределяет стандартную десериализацию.
Методы readObject* похожи на конструктор. Как и конструктор, они подвержены проблеме виртуального вызова. Как и конструктор, они используются для поддержания инвариантов класса (только для случая десериализации).
writeObject(ObjectOutputStream s) используется для записи собственной сериализационной формы.
Object readResolve() может использоваться для реализации какого-либо порождающего паттерна при десериализации. Даже при его использовании объект сначала будет десериализован, поэтому рекомендуется вместе с этим методом помечать все поля transient. Видимость этого метода для наследника определяет, будет ли наследник вызывать его.
Для подмены объекта при записи добавляется симметричный метод Object writeReplace().
Интерфейс Externalizable дает инструмент полной подмены реализации сериализации. Рассмотрим его в следующих постах.
Интерфейс Serializable пуст, но есть ряд методов, добавив которые в сериализуемый класс можно добиться изменения этапов процесса сериализации и десериализации.
readObjectNoData() используется для инициализации класса-родителя, который при сериализации оригинального объекта еще не был родителем. Подробное объяснение.
readObject(ObjectInputStream s) переопределяет стандартную десериализацию.
Методы readObject* похожи на конструктор. Как и конструктор, они подвержены проблеме виртуального вызова. Как и конструктор, они используются для поддержания инвариантов класса (только для случая десериализации).
writeObject(ObjectOutputStream s) используется для записи собственной сериализационной формы.
Object readResolve() может использоваться для реализации какого-либо порождающего паттерна при десериализации. Даже при его использовании объект сначала будет десериализован, поэтому рекомендуется вместе с этим методом помечать все поля transient. Видимость этого метода для наследника определяет, будет ли наследник вызывать его.
Для подмены объекта при записи добавляется симметричный метод Object writeReplace().
Интерфейс Externalizable дает инструмент полной подмены реализации сериализации. Рассмотрим его в следующих постах.
👍7