Как работает вывод типов?
Для начала разберемся, что такое вывод типов. 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
Как сериализация работает с наследованием?
Когда Serializable класс имеет цепочку родителей, пока эти родители тоже Serializable, десериализация объекта идет от родителя к наследнику, в обход конструктора. Вместо него вызываются методы readObject (readObjectNoData). Но как только встречается первый предок, не реализующий интерфейс Serializable, инициализация для него возвращается в нормальное русло – вместо readObject вызывается конструктор без аргументов. Если такого конструктора нет, или он объявлен private, исполнение выбросит InvalidClassException.
При сериализации несериализуемые предки просто игнорируются.
Если класс несериализуемый и не предоставляет достаточного доступа к своему логическому состоянию для наследников, правильно реализовать его наследника сериализуемым может быть невозможно.
Популярный вопрос на тему – как когда сериализуешь объект класса-наследника, избежать сериализации его родительской части. Единственный способ добиться этого – кастомизировать сериализационную форму, определив собственную реализацию writeObject(), либо используя интерфейс Externalizable.
Открытость класса для наследования делает неприменимым паттерн serialization proxy (который рассмотрим позднее).
Когда Serializable класс имеет цепочку родителей, пока эти родители тоже Serializable, десериализация объекта идет от родителя к наследнику, в обход конструктора. Вместо него вызываются методы readObject (readObjectNoData). Но как только встречается первый предок, не реализующий интерфейс Serializable, инициализация для него возвращается в нормальное русло – вместо readObject вызывается конструктор без аргументов. Если такого конструктора нет, или он объявлен private, исполнение выбросит InvalidClassException.
При сериализации несериализуемые предки просто игнорируются.
Если класс несериализуемый и не предоставляет достаточного доступа к своему логическому состоянию для наследников, правильно реализовать его наследника сериализуемым может быть невозможно.
Популярный вопрос на тему – как когда сериализуешь объект класса-наследника, избежать сериализации его родительской части. Единственный способ добиться этого – кастомизировать сериализационную форму, определив собственную реализацию writeObject(), либо используя интерфейс Externalizable.
Открытость класса для наследования делает неприменимым паттерн serialization proxy (который рассмотрим позднее).
👍8
3, 2 … Остановимся здесь.⤵️
Потому что 2 недель достаточно, чтобы познакомиться с основами Java и понять, подходит ли вам это направление.
Не обещаем, что будет легко, но интересно, полезно и недорого (всего 990 рублей!) – гарантируем.
Расклад такой: мы даем вам базу — больше 60 уроков, вебинары, лайвкодинг, а вы пишете собственную программу.
Если переживаете — не переживайте :) Рядом всегда будет наставник, готовый ответить на любой ваш вопрос.
⏰ Старт курса уже 6 марта, присоединяйтесь!
Потому что 2 недель достаточно, чтобы познакомиться с основами Java и понять, подходит ли вам это направление.
Не обещаем, что будет легко, но интересно, полезно и недорого (всего 990 рублей!) – гарантируем.
Расклад такой: мы даем вам базу — больше 60 уроков, вебинары, лайвкодинг, а вы пишете собственную программу.
Если переживаете — не переживайте :) Рядом всегда будет наставник, готовый ответить на любой ваш вопрос.
⏰ Старт курса уже 6 марта, присоединяйтесь!
👍4