Как Android-разработчику повысить квалификацию?
Прийти на практический бесплатный урок «Делаем простейшую имплементацию SDUI» от OTUS и Абакара Магомедова – Android-разработчика в «Альфа-банк».
👩💻На вебинаре разберем:
- в чем заключается Server Drvien UI подход;
- его плюсы и минусы;
- что нужно, чтобы реализовать простейшую его реализацию.
Занятие пройдёт 30 ноября в 20:00 мск и будет приурочено к старту курса «Android Developer. Professional». Это отличная возможность совершенно бесплатно протестировать формат обучения и задать преподавателю любые вопросы в режиме реального времени. После урока вы сможете стать студентом курса в рассрочку.
✍️ Для бесплатного участия и получения записи пройдите короткое тестирование прямо сейчас: https://otus.pw/fbLY/
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
Прийти на практический бесплатный урок «Делаем простейшую имплементацию SDUI» от OTUS и Абакара Магомедова – Android-разработчика в «Альфа-банк».
👩💻На вебинаре разберем:
- в чем заключается Server Drvien UI подход;
- его плюсы и минусы;
- что нужно, чтобы реализовать простейшую его реализацию.
Занятие пройдёт 30 ноября в 20:00 мск и будет приурочено к старту курса «Android Developer. Professional». Это отличная возможность совершенно бесплатно протестировать формат обучения и задать преподавателю любые вопросы в режиме реального времени. После урока вы сможете стать студентом курса в рассрочку.
✍️ Для бесплатного участия и получения записи пройдите короткое тестирование прямо сейчас: https://otus.pw/fbLY/
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
👍6🔥2
Как работают стримы?
Пакет java.util.stream – это средства потоковой обработки данных в функциональном стиле. Они не имеют ничего общего (кроме названия) с потоками ввода-вывода. Типичные применения – конвертация, переупаковка, и агрегация данных.
Три основных понятия Java Stream API – источник данных, промежуточная (intermediate), и терминальная (terminal) операции.
Источником может быть заранее заданный набор данных, или динамический генератор, возможно даже бесконечный. Сам источник никогда не модифицируется последующими операциями.
Промежуточные операции модифицируют стрим. На одном потоке можно вызвать сколько угодно промежуточных операций.
Терминальная операция «потребляет» поток. Она может быть только одна, в конце работы с отдельно взятым стримом. Стримы работают лениво – вся цепочка промежуточных операций не начнет выполняться до вызова терминальной.
Типичный пример использования стримов – map-reduce. Map – промежуточная операция, reduce – терминальная.
Источники и промежуточные операции могут изменять набор характеристик потока, которые влияют на дальнейшую обработку. Операция может иметь свойства – элементы перечисления StreamOpFlag:
• SORTED – можно сравнивать элементы;
• ORDERED – определен порядок обхода;
• DISTINCT – содержит уникальные элементы, без дублей;
• SIZED – имеет определенный размер;
• SHORT_CIRCUIT – операция, которая может приводить к короткому замыканию.
Пакет java.util.stream – это средства потоковой обработки данных в функциональном стиле. Они не имеют ничего общего (кроме названия) с потоками ввода-вывода. Типичные применения – конвертация, переупаковка, и агрегация данных.
Три основных понятия Java Stream API – источник данных, промежуточная (intermediate), и терминальная (terminal) операции.
Источником может быть заранее заданный набор данных, или динамический генератор, возможно даже бесконечный. Сам источник никогда не модифицируется последующими операциями.
Промежуточные операции модифицируют стрим. На одном потоке можно вызвать сколько угодно промежуточных операций.
Терминальная операция «потребляет» поток. Она может быть только одна, в конце работы с отдельно взятым стримом. Стримы работают лениво – вся цепочка промежуточных операций не начнет выполняться до вызова терминальной.
Типичный пример использования стримов – map-reduce. Map – промежуточная операция, reduce – терминальная.
Источники и промежуточные операции могут изменять набор характеристик потока, которые влияют на дальнейшую обработку. Операция может иметь свойства – элементы перечисления StreamOpFlag:
• SORTED – можно сравнивать элементы;
• ORDERED – определен порядок обхода;
• DISTINCT – содержит уникальные элементы, без дублей;
• SIZED – имеет определенный размер;
• SHORT_CIRCUIT – операция, которая может приводить к короткому замыканию.
👍15🔥2
Пройди тест по Java - проверь, готов ли ты
За 6 месяцев обучения:
- Научишься создавать современные java-приложения
- Поймешь основы функционирования JVM (сборка мусора, byteCode)
- Освоишь приемы применения многопоточности
- Напишешь выпускной проект на любую тему под наставничеством преподавателя
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ruPlease open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
Работа мечты — это не только про карьерные возможности, но и про команду, с которой вы на одной волне. Повезло ли вам встретить таких коллег или пока только сложили образ идеального коллектива?
Расскажите о нём нейросети Kandinsky 2.2, и она создаст портрет сокомандника, с которым точно сработаетесь. Кстати, возможно, он уже ждёт вас в Сбере — вместе с крутыми проектами и насыщенной корпоративной жизнью.
Расскажите о нём нейросети Kandinsky 2.2, и она создаст портрет сокомандника, с которым точно сработаетесь. Кстати, возможно, он уже ждёт вас в Сбере — вместе с крутыми проектами и насыщенной корпоративной жизнью.
❤6👍3🔥2🤣2🥴1
Как реализовать собственный стрим?
Любой стрим определяется его сплитератором. Spliterator – это специальный разделяемый внутренний итератор.
Есть много способов получить готовый сплитератор или стрим, но чтобы создать полностью свою специфическую логику перебора элементов, придется написать собственный сплитератор.
Поток создается из сплитератора одним из статических методов класса StreamSupport. Вызов его методов осуществляется самим фреймворком. Вкратце его работа выглядит так:
• Элементы перебираются методом tryAdvance, пока он не выдаст false. Через параметр action к элементу применяются последующие операции.
• При применении промежуточных и терминальных операций учитываются характеристики потока, изначально задаваемые методом characteristics.
• Когда обработка стрима распараллеливается, методом trySplit от начала последовательности элементов «откусывается» часть, и возвращается завернутой в новый сплитератор. Текущий продолжает идти по оставшемуся хвосту. В идеале, по возможности эта часть – половина элементов потока. Если разделить уже нельзя, возвращается null.
Любой стрим определяется его сплитератором. Spliterator – это специальный разделяемый внутренний итератор.
Есть много способов получить готовый сплитератор или стрим, но чтобы создать полностью свою специфическую логику перебора элементов, придется написать собственный сплитератор.
Поток создается из сплитератора одним из статических методов класса StreamSupport. Вызов его методов осуществляется самим фреймворком. Вкратце его работа выглядит так:
• Элементы перебираются методом tryAdvance, пока он не выдаст false. Через параметр action к элементу применяются последующие операции.
• При применении промежуточных и терминальных операций учитываются характеристики потока, изначально задаваемые методом characteristics.
• Когда обработка стрима распараллеливается, методом trySplit от начала последовательности элементов «откусывается» часть, и возвращается завернутой в новый сплитератор. Текущий продолжает идти по оставшемуся хвосту. В идеале, по возможности эта часть – половина элементов потока. Если разделить уже нельзя, возвращается null.
👍12🔥5
Как работают параллельные стримы?
Основная цель, ради которой в Java 8 был добавлен Stream API – удобство многопоточной обработки.
Обычный стрим будет выполняться параллельно после вызова промежуточной операции parallel(). Некоторые стримы создаются уже многопоточными, например результат вызова Collection#parallelStream(). Для распараллеливания используется единый общий ForkJoinPool.
Внутри реализации потока его сплиттератор оборачивается в AbstractTask, который и отправляется на выполнение в пул. AbstractTask при выполнении считывает estimateSize сплиттератора и текущую степень параллелизма пула. На основе этих данных он принимает решение, распараллелить ли сплиттератор на два методом trySplit().
У удобства такого решения есть обратная сторона. Так как пул единый, нагрузка распределяется на всех пользователей параллельных стримов в программе. Если в одном потоке выполняются долгие блокирующие операции, это может ударить по производительности в совершенно не связанном с ним другом потоке.
Если всё же требуется использовать отдельный пул потоков, сам стрим выполняется как задача этого отдельного пула.
Основная цель, ради которой в Java 8 был добавлен Stream API – удобство многопоточной обработки.
Обычный стрим будет выполняться параллельно после вызова промежуточной операции parallel(). Некоторые стримы создаются уже многопоточными, например результат вызова Collection#parallelStream(). Для распараллеливания используется единый общий ForkJoinPool.
Внутри реализации потока его сплиттератор оборачивается в AbstractTask, который и отправляется на выполнение в пул. AbstractTask при выполнении считывает estimateSize сплиттератора и текущую степень параллелизма пула. На основе этих данных он принимает решение, распараллелить ли сплиттератор на два методом trySplit().
У удобства такого решения есть обратная сторона. Так как пул единый, нагрузка распределяется на всех пользователей параллельных стримов в программе. Если в одном потоке выполняются долгие блокирующие операции, это может ударить по производительности в совершенно не связанном с ним другом потоке.
Если всё же требуется использовать отдельный пул потоков, сам стрим выполняется как задача этого отдельного пула.
👍12🔥2🎉1
Расскажем на бесплатном практическом уроке «Type classes в Scala» от OTUS и Алексея Воронца – руководителя разработки в NAUMEN.
— разберем type class паттерн в Scala;
— познакомимся с мотивацией его возникновения и задачами, которые он должен решать;
— сформулируем и разберем основные компоненты;
— решим ряд задач с использованием type class паттерна для закрепления на практике.
Занятие пройдёт 14 декабря в 20:00 мск и будет приурочено к старту курса «Scala-разработчик». После урока вы сможете продолжить обучение на специальных условиях.
👉 Для бесплатного участия и получения записи регистрируйтесь прямо сейчас: регистрация
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ruPlease open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥2
Как инстанцировать экземпляр 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() возвращает класс, которым параметризован родитель.
🔥17👍8👏1
Уже 29 декабря стартует новая группа специализации «Java Developer» в OTUS.
На онлайн-курсе вы получите:
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ruPlease open Telegram to view this post
VIEW IN TELEGRAM
👍5🤣3🔥2😁2
Как написать иммутабельный класс?
Immutable (неизменяемый) класс – это класс, состояние экземпляров которого невозможно изменить после создания.
С иммутабельным классом всегда легче работать. Его состояние не поменяется, значит обращаться к нему в многопоточной среде можно без дополнительной синхронизации. Функции, зависящие только от состояния экземпляра будут возвращать один и тот же результат от вызова к вызову – это облегчает например реализацию hashCode(). Также вместо нескольких одинаковых экземпляров можно использовать один закэшированный объект, экономя память (паттерн Приспособленец).
Шаги, которые необходимо предпринять, чтобы класс стал immutable:
1. Запретите расширение класса – либо объявите его final, либо закройте доступ наследникам ко всем способам мутации, перечисленным в следующих пунктах;
2. Сделайте все поля финальными;
3. Не выставляйте наружу методов-мутаторов, которые меняют состояние;
4. Не отдавайте наружу поля ссылочного изменяемого типа (объекты классов, массивы) – если объект под ссылкой не иммутабельный, должна возвращаться его глубокая копия (defensive copy);
5. Создавайте объект правильно (подробнее в следующем посте).
Если вам нужны преимущества иммутабельного объекта, но также нужно иногда изменять его, подойдет подход copy on write: каждый метод-мутатор должен мутировать и возвращать не сам объект, а только что созданную его копию. Оригинал всё так же остается неизменным.
Immutable (неизменяемый) класс – это класс, состояние экземпляров которого невозможно изменить после создания.
С иммутабельным классом всегда легче работать. Его состояние не поменяется, значит обращаться к нему в многопоточной среде можно без дополнительной синхронизации. Функции, зависящие только от состояния экземпляра будут возвращать один и тот же результат от вызова к вызову – это облегчает например реализацию hashCode(). Также вместо нескольких одинаковых экземпляров можно использовать один закэшированный объект, экономя память (паттерн Приспособленец).
Шаги, которые необходимо предпринять, чтобы класс стал immutable:
1. Запретите расширение класса – либо объявите его final, либо закройте доступ наследникам ко всем способам мутации, перечисленным в следующих пунктах;
2. Сделайте все поля финальными;
3. Не выставляйте наружу методов-мутаторов, которые меняют состояние;
4. Не отдавайте наружу поля ссылочного изменяемого типа (объекты классов, массивы) – если объект под ссылкой не иммутабельный, должна возвращаться его глубокая копия (defensive copy);
5. Создавайте объект правильно (подробнее в следующем посте).
Если вам нужны преимущества иммутабельного объекта, но также нужно иногда изменять его, подойдет подход copy on write: каждый метод-мутатор должен мутировать и возвращать не сам объект, а только что созданную его копию. Оригинал всё так же остается неизменным.
👍16
Что вы НЕ ЗНАЛИ о разработке ПО?
Расскажем на бесплатном открытом уроке «Методологии разработки ПО» от OTUS.
На вебинаре:
- рассмотрим различные подходы к разработке – водопадная модель, итеративная разработка, спиральная модель, гибкая разработка и др.;
- обсудим их особенности, ограничения и критерии выбора;
- изучим модели жизненного цикла – классическую и модель спирального развития.
Занятие пройдёт 18 декабря в 20:00 мск и будет приурочено к старту курса «Архитектура и шаблоны проектирования». После урока вы сможете продолжить обучение в рассрочку.
Для бесплатного участия и получения записи пройдите короткое тестирование прямо сейчас: https://otus.pw/QCNw/
Расскажем на бесплатном открытом уроке «Методологии разработки ПО» от OTUS.
На вебинаре:
- рассмотрим различные подходы к разработке – водопадная модель, итеративная разработка, спиральная модель, гибкая разработка и др.;
- обсудим их особенности, ограничения и критерии выбора;
- изучим модели жизненного цикла – классическую и модель спирального развития.
Занятие пройдёт 18 декабря в 20:00 мск и будет приурочено к старту курса «Архитектура и шаблоны проектирования». После урока вы сможете продолжить обучение в рассрочку.
Для бесплатного участия и получения записи пройдите короткое тестирование прямо сейчас: https://otus.pw/QCNw/
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru🔥4👍1😐1
Какой результат выведет следующая программа?
Anonymous Quiz
10%
0
16%
128
60%
-128
14%
Ошибку компиляции
🔥13👍3☃2😱1🍌1
На вебинаре разберём, как можно сделать самодельный appender для Logback, который будет отправлять логи в TCP-сервер логирования.
Также эксперт подробно ответит на любые возникающие вопросы.
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ruPlease open Telegram to view this post
VIEW IN TELEGRAM
👍4😁2🔥1
Как инициализировать иммутабельный класс?
Если прочитать определение immutable-объекта внимательно, момент его создания – это единственный этап в жизненном цикле, когда объект может меняться. Действительно, как иначе установить это неизменяемое состояние. Отсюда, при неправильной реализации, вытекают некоторые риски дальнейшей неизменяемости.
Первое, самое очевидное правило: копируйте поля ссылочных типов. Когда пользователь передал в конструктор изменяемый объект, возможно он все еще владеет ссылкой на него. Тогда, пользуясь этой ссылкой, пользователь может менять поле вашего иммутабельного объекта позднее. Создав копию объекта-параметра перед присвоением в поле, вы получите эксклюзивную ссылку, недоступную извне.
Вторая проблема, с которой вы гораздо менее вероятно столкнетесь, но о которой нужно знать: без должной синхронизации в многопоточной среде конструктор может получить ссылку на не до конца сконструированный объект. Для поддержки создания объектов в многопоточной среде нужно обеспечить создание его полей happens-before. Проблема аналогична double-checked блокировке без ключевого слова volatile – детали можно почитать здесь.
Что касается удобства создания immutable объектов. Если класс состоит из всего пары полей – дополнительные действия скорее всего не нужны, хватит обычного конструктора.
Однако, когда полей много, это становится проблемой. В Java, в отличие от таких языков как например TypeScript или Kotlin, нельзя указывать имена передаваемых параметров. Обычно можно воспользоваться сеттерами, но в неизменяемом классе их нет.
Похожая проблема возникает, когда процесс создания объекта не может быть атомарным, потому что он разделен между несколькими сущностями. Разные компоненты предоставляют разные поля, приходится копить их во временных переменных, прежде чем вызвать конструктор.
На помощь приходят порождающие паттерны. Самый распространенный подход – builder (паттерн Строитель). Для иммутабельного типа Foo создается дополнительный изменяемый класс FooBuilder. В билдере есть сеттеры для каждого поля. Эти сеттеры обычно возвращают this, что позволяет вызывать их в цепочке. Цепочка заканчивается методом build(), который уже создает иммутабельный Foo.
Каждый сеттер выполняет роль именованного параметра. Сам билдер, как обычный объект, можно передавать от компонента к компоненту, делегируя им части инициализации. Использование этого паттерна логически разделяет существование объекта типа Foo на два этапа – создание и использование.
Если прочитать определение immutable-объекта внимательно, момент его создания – это единственный этап в жизненном цикле, когда объект может меняться. Действительно, как иначе установить это неизменяемое состояние. Отсюда, при неправильной реализации, вытекают некоторые риски дальнейшей неизменяемости.
Первое, самое очевидное правило: копируйте поля ссылочных типов. Когда пользователь передал в конструктор изменяемый объект, возможно он все еще владеет ссылкой на него. Тогда, пользуясь этой ссылкой, пользователь может менять поле вашего иммутабельного объекта позднее. Создав копию объекта-параметра перед присвоением в поле, вы получите эксклюзивную ссылку, недоступную извне.
Вторая проблема, с которой вы гораздо менее вероятно столкнетесь, но о которой нужно знать: без должной синхронизации в многопоточной среде конструктор может получить ссылку на не до конца сконструированный объект. Для поддержки создания объектов в многопоточной среде нужно обеспечить создание его полей happens-before. Проблема аналогична double-checked блокировке без ключевого слова volatile – детали можно почитать здесь.
Что касается удобства создания immutable объектов. Если класс состоит из всего пары полей – дополнительные действия скорее всего не нужны, хватит обычного конструктора.
Однако, когда полей много, это становится проблемой. В Java, в отличие от таких языков как например TypeScript или Kotlin, нельзя указывать имена передаваемых параметров. Обычно можно воспользоваться сеттерами, но в неизменяемом классе их нет.
Похожая проблема возникает, когда процесс создания объекта не может быть атомарным, потому что он разделен между несколькими сущностями. Разные компоненты предоставляют разные поля, приходится копить их во временных переменных, прежде чем вызвать конструктор.
На помощь приходят порождающие паттерны. Самый распространенный подход – builder (паттерн Строитель). Для иммутабельного типа Foo создается дополнительный изменяемый класс FooBuilder. В билдере есть сеттеры для каждого поля. Эти сеттеры обычно возвращают this, что позволяет вызывать их в цепочке. Цепочка заканчивается методом build(), который уже создает иммутабельный Foo.
Каждый сеттер выполняет роль именованного параметра. Сам билдер, как обычный объект, можно передавать от компонента к компоненту, делегируя им части инициализации. Использование этого паттерна логически разделяет существование объекта типа Foo на два этапа – создание и использование.
👍11😁1
Старт подготовительного курса по Java-разработке.
⏰ Когда: уже 12 декабря.
Даем: 62 урока с практикой в браузере, 3 онлайн вебинара и 1 сессию лайвкодинга с практикующим разработчиком.
Получаем: крепкие знания базы языка, умение понимать код и первую программу на Java, написанную вместе с наставником.
Всего 990 ₽ и ваше жгучее желание начать. Приходите с любым бэкграундом: нам не важны возраст, пол, образование и текущая профессия.
Запишитесь прямо сейчас!
Реклама. АНПОО "ХЕКСЛЕТ КОЛЛЕДЖ". ИНН 7839056670.
⏰ Когда: уже 12 декабря.
Даем: 62 урока с практикой в браузере, 3 онлайн вебинара и 1 сессию лайвкодинга с практикующим разработчиком.
Получаем: крепкие знания базы языка, умение понимать код и первую программу на Java, написанную вместе с наставником.
Всего 990 ₽ и ваше жгучее желание начать. Приходите с любым бэкграундом: нам не важны возраст, пол, образование и текущая профессия.
Запишитесь прямо сейчас!
Реклама. АНПОО "ХЕКСЛЕТ КОЛЛЕДЖ". ИНН 7839056670.
👍4
Как реализовать метод equals?
Сначала нужно решить, действительно ли вам нужно переопределять equals(). Реализация по умолчанию делает объект равным только самому себе (сравнение на идентичность). Это имеет смысл, если у вашего класса не бывает отдельных, но логически одинаковых экземпляров.
Если два экземпляра всё-таки могут быть равны, equals() нужно переопределять. Реализация должна соблюдать контракт: это отношение эквивалентности (рефлексивность, транзитивность, симметричность), ни один объект не равен null.
Рефлексивность. первым делом проверим, не идентичен ли переданный объект текущему. Если да – сразу вернем true.
Неравенство null. Если аргумент null – сразу вернем false.
Симметричность. Если мы допускаем наследование и расширение метода equals(), в наследнике может появиться дополнительная логика, которая сделает !other.equals(this) при this.equals(other). Проще всего избежать этого, добавив сравнение типов. Если типы не равны – сразу вернем false. Почему не надо использовать instanceof.
Транзитивность. Оператор == обладает свойствами транзитивности и симметричности. Далее мы сравниваем на равенство все примитивные свойства. Для ссылочных типов этими характеристиками по контракту обладает equals – для сравнения ссылочных типов пользуемся им.
Речь здесь идет о логических свойствах. Фактически одно логическое свойство может быть представлено несколькими полями класса, или же может вычисляться на лету. Некоторые поля служат для внутренних технических нужд, и не имеют отношения к логическому состоянию. Такие поля обычно исключают из сравнения.
Сначала нужно решить, действительно ли вам нужно переопределять equals(). Реализация по умолчанию делает объект равным только самому себе (сравнение на идентичность). Это имеет смысл, если у вашего класса не бывает отдельных, но логически одинаковых экземпляров.
Если два экземпляра всё-таки могут быть равны, equals() нужно переопределять. Реализация должна соблюдать контракт: это отношение эквивалентности (рефлексивность, транзитивность, симметричность), ни один объект не равен null.
Рефлексивность. первым делом проверим, не идентичен ли переданный объект текущему. Если да – сразу вернем true.
Неравенство null. Если аргумент null – сразу вернем false.
Симметричность. Если мы допускаем наследование и расширение метода equals(), в наследнике может появиться дополнительная логика, которая сделает !other.equals(this) при this.equals(other). Проще всего избежать этого, добавив сравнение типов. Если типы не равны – сразу вернем false. Почему не надо использовать instanceof.
Транзитивность. Оператор == обладает свойствами транзитивности и симметричности. Далее мы сравниваем на равенство все примитивные свойства. Для ссылочных типов этими характеристиками по контракту обладает equals – для сравнения ссылочных типов пользуемся им.
Речь здесь идет о логических свойствах. Фактически одно логическое свойство может быть представлено несколькими полями класса, или же может вычисляться на лету. Некоторые поля служат для внутренних технических нужд, и не имеют отношения к логическому состоянию. Такие поля обычно исключают из сравнения.
👍8🥰5❤3
Команда Поиска и рекламных технологий Яндекса приглашает на митап для senior бэкенд-разработчиков в Екатеринбурге.
Поговорим про разработку, поиграем в квиз, понетворкаемся и просто хорошо проведем время.
12 декабря, 18:00.
Регистрируйтесь, количество мест в офлайне ограничено!
Поговорим про разработку, поиграем в квиз, понетворкаемся и просто хорошо проведем время.
12 декабря, 18:00.
Регистрируйтесь, количество мест в офлайне ограничено!
🔥3👍2
Как реализовать метод hashCode?
Если вы переопределили equals(), то обязательно также переопределить и hashCode(). Это не просто теоретическое требование. Если класс нарушает это правило, хранение его экземпляров в качестве например ключей HashMap приводит к непредсказуемому поведению.
Результат hashCode() должен быть одинаковый для равных в смысле equals объектов. Обычно для этого значение хэш-кода вычисляется на основе значений полей, которые участвуют в equals(). Но и возвращение одной и той же константы 42 для любого экземпляра класса тоже будет валидной реализацией.
Результат hashCode() должен быть равномерно распределен. Это правило не такое строгое как остальные. Его нарушение не сломает программу, хотя может сильно ухудшить производительность. Поэтому константа 42 – допустимая, но не лучшая идея. Вместо этого все значения полей сначала приводятся к int: boolean превращается в любую пару констант, null в 0, для ссылочных типов берется их hashCode(). Затем все эти значения смешиваются с помощью бинарного оператора XOR (^). Дополнительно для лучшего распределения можно применять битовые сдвиги. Если вы владеете информацией о распределении значений полей в конкретно вашем случае, эту реализацию можно улучшить.
Результат hashCode() должен быть одинаковый на протяжении времени жизни объекта. Если вычисление хэш-кода зависит от переменных значений, сохраните его значение во внутреннее поле при первом вызове. При следующих вызовах сразу возвращайте это закэшированное значение.
Если вы переопределили equals(), то обязательно также переопределить и hashCode(). Это не просто теоретическое требование. Если класс нарушает это правило, хранение его экземпляров в качестве например ключей HashMap приводит к непредсказуемому поведению.
Результат hashCode() должен быть одинаковый для равных в смысле equals объектов. Обычно для этого значение хэш-кода вычисляется на основе значений полей, которые участвуют в equals(). Но и возвращение одной и той же константы 42 для любого экземпляра класса тоже будет валидной реализацией.
Результат hashCode() должен быть равномерно распределен. Это правило не такое строгое как остальные. Его нарушение не сломает программу, хотя может сильно ухудшить производительность. Поэтому константа 42 – допустимая, но не лучшая идея. Вместо этого все значения полей сначала приводятся к int: boolean превращается в любую пару констант, null в 0, для ссылочных типов берется их hashCode(). Затем все эти значения смешиваются с помощью бинарного оператора XOR (^). Дополнительно для лучшего распределения можно применять битовые сдвиги. Если вы владеете информацией о распределении значений полей в конкретно вашем случае, эту реализацию можно улучшить.
Результат hashCode() должен быть одинаковый на протяжении времени жизни объекта. Если вычисление хэш-кода зависит от переменных значений, сохраните его значение во внутреннее поле при первом вызове. При следующих вызовах сразу возвращайте это закэшированное значение.
👍15🔥4❤1