Java: fill the gaps
前往频道在 Telegram
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк 🔥Тот самый курс по многопочке🔥 https://fillthegaps.ru/mt Комплименты, вопросы, предложения: @utki_letyat
显示更多📈 Telegram 频道 Java: fill the gaps 的分析概览
频道 Java: fill the gaps (@java_fillthegaps) 俄语 语言赛道中的 是活跃参与者。目前社区聚集了 12 397 名订阅者,在 技术与应用 类别中位列第 9 827,并在 俄罗斯 地区排名第 51 938 位。
📊 受众指标与增长动态
自 невідомо 创建以来,项目保持高速增长,吸引了 12 397 名订阅者。
根据 26 八月, 2026 的最新数据,频道保持稳定运转。过去 30 天订阅人数变化为 -57,过去 24 小时变化为 -1,整体触达仍然可观。
- 认证状态: 未认证
- 互动率 (ER): 平均受众互动率为 0%。内容发布后 24 小时内通常能获得 N/A% 的反应,占订阅者总量。
- 帖子覆盖: 每篇帖子平均可获得 0 次浏览,首日通常累积 0 次浏览。
- 互动与反馈: 受众积极参与,单帖平均反应数为 0。
- 主题关注点: 内容集中在 redis, hashmap, linkedhashmap, индекс, фича 等核心主题上。
📝 描述与内容策略
作者将该频道定位为表达主观观点的平台:
“Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк
🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt
Комплименты, вопросы, предложения: @utki_letyat”
凭借高频更新(最新数据采集于 27 八月, 2026),频道始终保持新鲜度与高覆盖。分析显示受众积极互动,使其成为 技术与应用 类别中的关键影响点。
12 397
订阅者
-124 小时
-147 天
-5730 天
帖子存档
12 396
Небольшой апдейт: в scala список односвязный
Спасибо неравнодушным подписчикам!
12 396
Списки, часть 2: что и когда использовать
В большинстве статей пишут, что LinkedList подходит для частой вставки или удаления элементов. На JavaRush также упоминают обход списка с периодической вставкой.
В теории всё так, но обычно в энтерпрайзе менее изысканные задачи:
🔸 Заполнить список
🔸 Отсортировать (встроенной функцией, конечно)
🔸 Обойти все элементы
🔸 Сделать что-нибудь с помощью Stream API
Для этих сценариев я сделала бенчмарки. У Stream API выделила 4 случая:
▫️ Простой однопоточный: stream→filter→collect
▫️ Сложный однопоточный: stream→map→filter→map→filter→collect
▫️ Простой с опцией parallel()
▫️ Сложный с опцией parallel()
Начнём с заполнения списка.
В прошлом посте выяснили, что до 100к элементов между списками почти нет разницы. ArrayList тратит много времени на копирование, а LinkedList — на создание обёрток и соединение ссылок.
Если количество элементов известно заранее, его можно указать в конструкторе:
List list = new ArrayList(25);Без лишнего копирования и переносов разница в скорости становится ошеломительной — список заполняется на 20-80% быстрее! Также ArrayList однозначно победил в номинациях: 🔹 Сортировка 🔹 Обход через цикл for 🔹 Простые Stream API 🔹 Любые Stream API с опцией parallel() В сложных однопоточных Stream API большая часть вычислений идёт на что-то полезное, и влияние оверхеда снижается. Ожидаемо:) Ещё факты против LinkedList: 🔸 Большинство классов JDK используют ArrayList для внутренних задач 🔸 Joshua Bloch (автор класса и книжки Effective Java) в 2015 написал твит:
"Does anyone actually use LinkedList? I wrote it, and I never use it."Но аналоги LinkedList иногда встречаются. В Scala для неизменяемого списка за основу взят именно двусвязный. Возможно, это лучше для каких-то сценариев, но пока не знаю для каких. На картинке часть моих бенчмарков. N — количество элементов в списке. Зелёный цвет обозначает победителя в категории. Ярко-зелёный — разница значений более 50%. ❗️Результаты на разных железках могут отличаться ❗️
12 396
Списки, часть 1: строение и основные операции
1. Внутреннее устройство
В сердце ArrayList лежит массив:
Object[] elementData;Размер массива задаётся в конструкторе:
new ArrayList(50). Значение по умолчанию — пустой массив.
Структура LinkedList чуть сложнее. Каждый элемент оборачивается в класс:
private static class Node {
E item;
Node next;
Node prev;
}
Т.е сам элемент списка + указатели на следующий и предыдущий элемент.
В объекте LinkedList хранится ссылка на первый и последний элемент списка:
Node first; Node last;2. Доступ по индексу: list.get(i) или list.set(i) ArrayList просто обращается по индексу массива LinkedList идёт долгим путём. Берёт элемент first идёт по ссылкам, пока не дойдёт до i-го элемента. Затем либо возвращает значение, либо обновляет. Кажется, что второй подход гораздо дольше. И это правда🙂 Чтобы получить 5000-ый элемент в списке из 10к элементов ArrayList тратит 6 наносекунд, а LinkedList — 8750. Чем больше элементов, тем больше разница. 3. Вставка в середину списка ArrayList Допустим, для списка 🟧🟧🟧🟧 вызвали метод add(2, 🦄) Создаётся новый массив размером +1: ⬜️⬜️⬜️⬜️⬜️ Все элементы старого списка копируются туда так, чтобы образовалось свободное место для нового элемента: 🟧🟧⬜️🟧🟧 Обновляем элемент: 🟧🟧🦄🟧🟧 LinkedList Рассмотрим тот же метод add(2, 🦄): ▫️ Создаём элемент списка с двумя ссылками: ⬅️🦄➡️ ▫️ Идём до текущего элемента 2 и получаем ссылки на элементы 1 и 3 ▫️ У элемента 1 обновляем ссылку на next, у 3 — на prev Само добавление простое, но перед ним нужно пройтись по списку. Это долго, поэтому LinkedList и здесь проигрывает по скорости. 4. Заполнение списка (см код в вопросе перед постом) ArrayList ▫️ Создаётся пустой массив ▫️ При первом add размер увеличивается до 10 ▫️ При добавлении 11 элемента создаётся новый массив размером 15. Предыдущие элементы копируются, затем добавляется новый ▫️ И так далее: когда места не хватает, создаётся массив в 1.5 раза больше LinkedList У текущего tail элемента обновляется ссылка next. Новый объект записывается как tail. Что работает быстрее? Интуиция подсказывает, что LinkedList, так как для ArrayList нужно часто переносить элементы в новый массив. На самом деле операции копирования выполняются быстро, так как элементы лежат в памяти рядом, а у процессоров хорошая поддержка этой операции. Бенчмарки показывают, что до 100к элементов разницы нет, а потом побеждает ArrayList. А при миллионе элементов ArrayList копируется реже и в итоге заполняется в два раза быстрее. Внизу таблица JMH бенчмарков с моего компьютера. На других железках результаты могут отличаться. Ответ на вопрос перед постом: для 50к элементов время почти одинаковое.
12 396
Списки — простая структура данных и популярная тема на собеседованиях.
Просят рассказать про строение, оценить сложность операций и случаи использования. Отвечаешь, что в
ArrayList быстрый доступ по индексу, а в LinkedList легко вставлять элементы в середину списка. Получаешь оффер🙂
Но это немного скучно, поэтому решила описать более жизненные кейсы и подробнее написать про строение. И самое главное — измерить время работы операций!
🔸 Часть 1: вводная. Строение списков и основные операции
🔸 Часть 2: жизненная. Что и когда использовать в коде12 396
Сбой в работе Wildberries
Сегодня необычный пост — расскажу вам новости и слухи🙂
С 14 марта в работе Wildberries наблюдались сбои — главная страница открывается, но пропали данные о прошлых заказах, новые заказы не оформляются, а в личном кабинете не отображается имя пользователя. Компания пишет о технических сбоях, но в интернете ходят слухи, что это хакерская атака.
Известных деталей мало, а я не спец в кибербезопасности. Пишу свои предположения на основе слухов и похожих случаев. На самом деле может быть по-другому.
Вероятный ход событий:
1️⃣ Сотрудник в Wildberries получил письмо со ссылкой, щёлкнул по ней и скачал дроппер.
Дроппер — программа, которая устанавливает другое вредоносное ПО и по возможности нейтрализует защиту ОС. В Windows дропперы часто выключают службу, которая предупреждает о попытках внести изменения в систему.
Письмо выглядит правдоподобно — красивая разметка, официальный стиль, актуальные новости. Это самое популярное начало атаки:
🔸 В начале пандемии COVID-19 компаниям приходили "рекомендации" по безопасной работе
🔸 Руководителю одного банка пришло письмо “журналистки РБК” с просьбой об интервью. Ссылка ведёт якобы на календарь, чтобы назначить удобное время
🔸 Во время белорусских протестов компаниям-партнёрам приходила рассылка: "Мы бастуем, поэтому к вам может прийти прокуратура с проверкой. Посмотрите по ссылке список интересующих их документов"
2️⃣ Сотрудник вышел из корпоративной сети, и дроппер скачал вредоносное ПО, который настроил удалённый доступ к компьютеру сотрудника.
Но может сотрудник сделал всё сам. Плохие люди тоже встречаются.
3️⃣ В продакшн внедряются маячки из Cobalt Strike
Cobalt Strike — это легальный инструмент тестирования безопасности, используется как безопасниками, так и хакерами. По статистике около 2/3 атак на крупные компании выполняется с помощью Cobalt
4️⃣ Удаляются резервные копии
5️⃣ Оставшиеся данные шифруются
6️⃣ Компании приходит письмо
"Пришлите столько-то биткоинов на этот кошелёк и получите дешифратор".
Иногда добавляются угрозы рассекретить информацию. Дальше всё зависит от инфраструктуры. Если у компании все бэкапы в одной подсети, и они уничтожены, то дело плохо. Всё хорошо, если остались репликации в других местах.
❓ Что с картами?
Вайдберрис пишет, что эти данные защищены по стандартам PCI DSS. Но по слухам в Даркнет утекли данные 100к российских карт за последние 3 дня. Я на всякий случай перевыпустила свою карту.
Надеюсь, в ближайшее время появится больше информации, и сама компания опишет ситуацию из первых рук. Тогда опыт Wildberries поможет другим компаниям🙏
12 396
Работа с задачами в Intellij iDEA
Полезно для тех, кто часто переключается между задачами.
1️⃣ Для энтерпрайза
В IDEA можно присоединиться к JIRA или другому таск трекеру, и у каждой задачи автоматически будет свой контекст.
Контекст — это открытые вкладки и git ветка.
Переключаетесь на другую задачу — автоматически открывается её набор вкладок и меняется ветка в гите.
Есть интеграция с популярными таск трекерами: JIRA, Trello, Github, Bugzilla и тд
Как подключить:
File → Settings → Tools → Tasks → Servers → Плюсик в правом верхнем углу
2️⃣ Для личных проектов
Можно сделать свой таск трекер внутри идеи:
Tools → Tasks & Contexts → Open Task
Дальше вводите название задачи. Их список затем доступен в правом верхнем углу.
ОЧЕНЬ полезно, если вы часто переключаетесь между задачами и цените свои вкладочки🥰
У таск трекера в IDEA есть близкая фича — можно следить, сколько времени потрачено на каждую задачу:
File → Settings → Tools → Tasks → Time Tracking → Enable Time Tracking
IDEA показывает только полное время работы. Нельзя посмотреть статистику за день и внести её в корпоративный трекер, так что для рабочих проектов эта фича скорее бесполезна.
12 396
Основы Spring: жизненный цикл бина
На тему жизненного цикла есть два доклада Жени Борисова:
— Spring потрошитель (часть 1, часть 2)
— Spring построитель
Доклады замечательные, и документация у Spring прекрасная. Но у меня в своё время картинка не сложилась, пока я не составила простую схему. Немножко обновила её с тех времён, чтобы с вами поделиться🙂 Опишу стандартный случай — Spring Boot 5, базовые конфигурации, все бины определяются в коде и связываются через @Autowired.
Основные участники:
🔸 Bean/бин/компонент — объект, которым управляет Spring
🔸 BeanDefinition — информация о будущем объекте. Имя класса, параметры конструктора и тд.
🔸 BeanFactory — отвечает за создание и хранение бинов
🔸 BeanPostProcessor (далее BPP) модифицирует бин после создания. У него два метода:
—
postProcessBeforeInitialization
— postProcessAfterInitialization
🔸 ApplicationContext — главный объект, хранит и вызывает все штучки выше
Теория
Что происходит и как туда внедриться:
1️⃣ Считывается информация из xml, yaml файлов
Создаётся список BeanDefinition. Интерфейс — BeanDefinitionReader
2️⃣ Обработка BeanDefinition
Дополняются параметры, определяется, кто от кого зависит и в каком порядке создавать бины. Интерфейс — BeanFactoryPostProcessor
3️⃣ Создаются экземпляры бинов
Сначала создаются объекты @Component, потом @Bean (из классов-конфигураций). Для каждого бина порядок такой:
▫️ Вызов конструктора
▫️ Проставляются @Autowired зависимости над полями
▫️ Вызов set методов с @Autowired
▫️ Вызов методов интерфейсов *Aware
▫️ Все BPP, метод postProcessBeforeInitialization
▫️ Метод с аннотацией @PostConstruct
▫️ Метод afterPropertiesSet (если класс реализует интерфейс InitializingBean)
▫️ Метод, определенный в @Bean(initMethod = "xxx")
▫️ Все BPP, метод postProcessAfterInitialization. Тут обычно создаются прокси-классы
▫️ Бин добавляется в контейнер
4️⃣ Событие “контекст обновился”
Ловится через реализацию интерфейса ApplicationListener<ContextRefreshedEvent>
Или через метод с аннотацией
@EventListener public void xxx(ContextRefreshedEvent contextRefreshedEvent)5️⃣ Запускается сервер 6️⃣ Событие "приложение запущено" Ловим событие
ApplicationStartedEvent как в пункте 5
7️⃣ Логика в методе main, если у вас Spring Boot
Менее популярные точки расширения:
▪️ Реализовать интерфейс *Aware
▪️ Поймать другие Spring Boot и Spring Core события
Практика
Точек расширения много, но что обычно используется:
🔸 Создать прокси-класс — BeanPostProcessor, метод after
🔸 Выполнить какую-то логику ДО старта сервера — поймать событие ContextRefreshedEvent
🔸 ПОСЛЕ старта сервера — событие ApplicationStartedEvent
Часто разогрев кэша, service discovery и подобные задачи помещают в @PostConstruct, но на момент вызова не все бины могут быть готовы. При работе с событиями этой проблемы нет.
И ещё пара рекомендаций:
🔹 Использовать одинаковые механизмы для одинаковых задач
🔹 Если в проекте больше трёх процессоров или задач на старте, упростите наблюдение за магией спринга. Это может быть файлик с описанием всех процессоров, очерёдностью и важными деталями. Или все процессоры поместить в одну папку.
🔹 Не помещайте бин процессоры и прослушивание событий в классы с бизнес-логикой. Нарушается принцип единственной ответственности, также могут быть проблемы с порядком обработки или количеством вызовов.
———
И небольшой апдейт по курсу: продлила скидос до 11 марта!12 396
Основы Spring: что там происходит
Когда я была джуниором, у меня было два этапа отношения к спрингу. Сначала "Вау, ставишь аннотацию и всё работает🥰".
Но однажды аннотация не сработала. Было сложно разобраться, в чём дело, и уже через неделю я считала Spring порождением сатаны.
Мне тогда не хватало общей картины, поэтому постараюсь составить её в этом посте.
Spring стоит на трёх китах:
1️⃣ Dependency Injection
Объекты в программе условно делятся на два вида:
▫️Компоненты — объекты, которые создаются в начале работы программы и обычно живут до конца. Контроллеры, классы сервисов, пулы соединений и т.д
▫️Данные — объекты, которые передаются между компонентами. Данные запросов, объекты пользователей, локальные переменные. Обычно живут в рамках одного запроса.
Spring берёт на себя создание и связывание компонентов(или бинов). Это называется DI и упрощает жизнь разработчику.
Spring — не единственный DI фреймворк. Подобные функции есть у Play framework, Dropwizard, Google Guice и т.д. Их часто используют тестировщики для автотестов.
2️⃣ Готовые компоненты
Транзакции, асинхронное выполнение, ограничение доступа, работа с БД и очередями, облачные задачи.
Spring предлагает готовые компоненты для большинства энтерпрайзных задач. Также Spring активно использует паттерн Декоратор (Wrapper, Proxy). От разработчика требуется минимум действий, которые хорошо описаны в мануалах.
Как это выглядит. Допустим, в вашем классе есть метод doWork() с аннотацией Transactional. Spring сделает следующее:
🔸 Создаст прокси класс и переопределит метод:
public void doWork() {
// начать транзакцию
super.doWork();
// закончить транзакцию
}
🔸 Создаст компонент с пулом транзакций и несколько промежуточных объектов.
✅ Минимум усилий от разработчика
❌ Сложно исправлять проблемы
❌ Большие накладные расходы. Spring создаёт и связывает сотни объектов на старте приложения, поэтому долго стартует и занимает много памяти
3️⃣ Кастомизация
Можно написать свои аннотации или переопределить стандартное поведение. Не могу сказать, что это нужно каждый день, но раз в полгода-год появляются такие задачи. У спринга можно встроиться почти куда угодно. Это мы подробно обсудим в следующем посте.
✅ Легко встроиться в процесс
❌ Из-за этого сам процесс усложняется
Именно комбо 1, 2 и 3 делает Spring таким популярным. Другие фреймворки предоставляют 1-2 пункта, но все три — только Spring.
Самые популярные модули — MVC, Security, Data и Cloud. У каждого из них под капотом целый мир, и об этом тоже как-нибудь напишу.12 396
Неделю назад я выложила анонс курса по многопоточке и получила немножко негатива на этот счёт.
Я против любых убийств и любой дискриминации. Текущая ситуация — катастрофа для всех. Но в этом канале я пишу для java разработчиков и делюсь своим опытом. Если вам кажется это неуместным — отпишитесь.
Курс тоже пройдёт по плану. Не вижу смысла его отменять.
Ну а теперь к постам⬇️
12 396
Анонс курса по многопоточке
Привет! Момент для анонса не самый удачный, но я надеюсь на лучшее🙏 Следую своему плану и стартую набор на курс по многопоточке!
Кто давно ждал и уже готов → вам сюда
А для тех, кто на канале недавно, расскажу подробнее.
Курс строится вокруг java.util.concurrent. Изучим его в мельчайших деталях, как это перекладывается на практику и сравним разные инструменты между собой. Затронем и смежные темы: тестирование, флажки JVM, виртуальные потоки и реактивное программирование.
Уроки выходят по расписанию 3 раза в неделю в предзаписи. Разберём огромное количество кейсов, лучших практик и возможных ошибок. Информации хватит для большинства многопоточных задач в энтерпрайзе.
Уровень: middle/senior
Старт: 21 марта
Длительность: 8 недель
Полная программа, отзывы(!) и запись тут: http://fillthegaps.ru/mt4
Зачем идти?
✅ Закрыть пробелы в базе. Java.util.concurrent — важная часть Java Core
✅ Работать на любых проектах. Многопоточка есть не только в хайлоад сервисах, а вообще везде. Просто обычно она прячется под слоем абстракций.
✅ Выбирать оптимальное решение для многопоточных задач, а не первое работающее со StackOverflow
✅ Круто выглядеть на собеседовании. Мало кто знает, зачем нужна модель памяти и как с ней работать на практике. Кандидат, который может это рассказать, сразу попадает в категорию "интересный"🙂
Сколько стоит?
До 3 марта действует скидос:
🔸Тариф без обратной связи: 30000 23900
🔸Тариф с обратной связью: 45000 36900
Курс можно оплатить за счёт компании. Пусть ваши коллеги напишут на diana@fillthegaps.ru
Если вы хотите закрыть этот пробел и разобраться с многопоточкой, и вам близок мой стиль изложения — записывайтесь, будет интересно!
http://fillthegaps.ru/mt4
12 396
Настоящий сеньор
Иногда в айтишных разговорах проскакивают фразы:
— Я уже полгода сеньор, но чувствую себя мидлом
— Вроде сеньор, но задачи делаю те же, что и раньше
— Мне слишком много платят за ту ерунду, которую я делаю
Это типичный синдром самозванца, и жить с ним неприятно. Что может помочь:
1️⃣ Признать текущие заслуги
"Настоящий сеньор" — это как настоящий мужчина. Как будто он делает что-то особенное, чтобы стань "настоящим".
Сеньором не делают заранее, должность выдают по факту. Будучи мидлом вы уже делали сеньорные задачи. Так что на текущем проекте вы точно на своём месте👍
2️⃣ Следить за общей ситуацией
Даже если не хотите менять работу, полезно раз в полгода-год следить за рынком:
◾️ Обновить резюме и вписать последние достижения
◾️ Посмотреть 20 вакансий HeadHunter и наметить план развития
◾️ Походить по собеседованиям и увидеть свои пробелы. Если получится оффер, то самооценка взлетит до небес🙂
В одной компании тебя зовут в СТО, в другой даже джуниором не возьмут. Все проекты разные, и требования везде разные. Расскажу своё видение, чем отличается сеньор от мидла:
Hard skills
▪️ Ориентироваться в популярных технологиях. Зачем нужны, плюсы-минусы, аналоги. Список технологий можно взять здесь. Он 2020 года, но ситуация не слишком поменялась
▪️ Настроить простой CI
▪️ Поднять систему с нуля с простой архитектурой (микросервисы, БД, очереди)
▪️ Работать с перфоманс проблемами. Знать, где посмотреть логи, что смотреть и как проверить, что проблема ушла
▪️ Целостное восприятие IT
Тут сложно дать конкретный список. Просто очень странно выглядит сеньор, который не знает, что такое DNS и как масштабировать БД.
Soft skills
▪️ Самостоятельность
Сеньор может взять любую задачу и продвинуть её решение. Прикинуть, на каком уровне решать задачу (на фронте, в бизнес-логике, поправить конфиги, исправить ошибку в БД). Разбить на подзадачи и определить приоритеты. Сформулировать вопрос, если не хватает данных или компетенций. Предложить разные решения для разных требований
▪️ Контроль джуниоров и мидлов — провести онбординг и код-ревью, понятно отвечать на вопросы и делегировать подходящие задачи
▪️ Психологическая зрелость (не знаю как ещё назвать) — принимать решения в условиях неопределённости, признавать свою неправоту и незнание. Не заметать проблемы под ковёр и понимать приоритеты. Уважительно и продуктивно общаться. Понимать, когда устал/раздражён, и не принимать серьёзных решений в этом состоянии. Спокойно удалять свой код, если он не нужен.
Но ещё раз — чётких стандартов нет. Каждый, кто уже стал сеньором, заслужил этот грейд, проделал большой путь и хорошо делал свою работу🔥
12 396
Интерфейсы, часть 3: разница с абстрактным классом
Отличия интерфейса и абстрактного класса — супер популярный вопрос на собеседовании. Во времена java 7 ответ был простой: "В интерфейсе нет реализаций".
Сейчас мир стал сложнее, и в интерфейсе есть приватные, статические и дефолтные методы с готовой реализацией. Это сближает интерфейсы с абстрактными классами.
❓В чём теперь разница интерфейса и абстрактного класса?
🛠 Функциональность
В абстрактный класс можно добавить многое:
▪️Конструктор
▪️Реализация экземплярных методов
▪️Нестатические поля
▪️private static поля
▪️Модификаторы final, synchronized, protected
Интерфейс сильно ограничен:
▪️Только статические поля
▪️Методам с реализацией недоступны экземплярные поля, поэтому их возможности слабее
✍🏻 Синтаксис
Абстрактный класс:
▫️ Ключевое слово
extends
▫️ В имени часто содержится Abstract, Template, Base
▫️ Класс реализует не больше одного абстрактного класса
Интерфейс:
▫️ Ключевое слово implements
▫️ Класс может реализовать несколько интерфейсов
▫️ 5 лет назад интерфейсам было модно добавлять суффикс able: Iterable, Comparable. Норма сегодняшнего дня — называть интерфейс по тем же правилам, что и класс.
💫 Назначение
Абстрактный класс — шаблон класса. Вспомогательная структура, чтобы не дублировать код в классах одной иерархии.
Интерфейс описывает методы для верхнеуровнего взаимодействия с классом, модулем или системой.
⭐️ Репутация
Абстрактный класс сокращает объем необходимого кода, когда иерархия классов конечна или известна заранее. Большое количество абстрактных классов считается анти-паттерном, у такой системы плохая читаемость, и в неё сложно вносить изменения.
Интерфейс используется в большинстве паттернов GoF и принципах SOLID. Поддерживает инкапсуляцию.12 396
Интерфейсы, часть 2: методы по умолчанию
Когда готовила материал по дефолтным методам, то перечитала почти все переписки разработчиков этой фичи. Огромное удовольствие — следить за спором опытных людей с высокой инженерной культурой и мощными теоретическими знаниями. Профессионализм завораживает🥰
Но я расскажу вам итог. В java 8 появилась новая возможность — методы с заданной реализацией:
interface Adapter {
default int get() {…}
}
Класс, который реализует интерфейс, переопределяет такой метод при необходимости.
❓Зачем?
1️⃣ Облегчить изменения API
Во времена java 7 в интерфейсах были только определения методов:
interface Collection<Т> {
void add(Т);
Т get();
}
Чтобы добавить, удалить или поменять сигнатуру метода нужно одновременно поменять код и в интерфейсе, и во всех реализациях. Если всё в рамках одного проекта, то проблем нет. Но если мы пишем библиотеку, то задача усложняется. Пользователи могут столкнутся с проблемами совместимости при переходе на новую версию.
Задача дефолтных методов — сгладить этот процесс, предоставить приемлемую или временную альтернативу. В JDK основная цель дефолтных методов — поддержка Stream API в коллекциях.
2️⃣ Вспомогательные методы
Которые не входят в прямую функциональность интерфейса, но их удобно добавить сюда, а не в каждый класс реализации. Нарушается принцип Interface segregation и часто похоже на костыль. Не одобряю, но такое встречается🙂
3️⃣ Комбинации базовых методов
Мы обсуждали это в прошлом посте. В интерфейсе есть методы, которые по сути — комбинация других методов этого же интерфейса.
Для таких комбинаций можно сделать статический метод. Он вызываются через имя интерфейса и недоступен у экземпляров. Если это неудобно, метод можно сделать как метод по умолчанию.
Пример: интерфейс Comparator
Цепочка из 2 компараторов — это 2 вызова compare и объединение результатов. Логика всегда одинакова, и нет смысла дублировать её в каждом подклассе:
default Comparator thenComparing(Comparator) {…};
❓Что если класс реализует 2 интерфейса с методами по умолчанию?
Правила такие:
🔸 Если в классе переопределён метод по умолчанию — используется метод класса
🔸 Если один интерфейс наследуется от другого — используется метод наследника
🔸 В остальных случаях — ошибка компиляции
Отсюда ответы на вопросы перед постом:
1️⃣ Ошибка компиляции
2️⃣ Напечатается В12 396
Вопрос 2. Что выведется в консоль?
(интерфейс В теперь расширяет интерфейс А)
