Java: fill the gaps
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк 🔥Тот самый курс по многопочке🔥 https://fillthegaps.ru/mt Комплименты, вопросы, предложения: @utki_letyat
إظهار المزيد📈 نظرة تحليلية على قناة تيليجرام Java: fill the gaps
تُعد قناة Java: fill the gaps (@java_fillthegaps) في القطاع اللغوي الروسية لاعباً نشطاً. يضم المجتمع حالياً 12 548 مشتركاً، محتلاً المرتبة 10 113 في فئة التكنولوجيات والتطبيقات والمرتبة 52 819 في منطقة روسيا.
📊 مؤشرات الجمهور والحراك
منذ تأسيسه في невідомо، حقق المشروع نمواً سريعاً وجمع 12 548 مشتركاً.
بحسب آخر البيانات بتاريخ 08 يونيو, 2026، تحافظ القناة على نشاط مستقر. خلال آخر 30 يوماً تغيّر عدد الأعضاء بمقدار -43، وفي آخر 24 ساعة بمقدار -3، مع بقاء الوصول العام مرتفعاً.
- حالة التحقق: غير موثّقة
- معدل التفاعل (ER): يبلغ متوسط تفاعل الجمهور 34.73%. وخلال أول 24 ساعة من النشر يحصد المحتوى عادةً N/A% من ردود الفعل نسبةً إلى إجمالي المشتركين.
- وصول المنشورات: يحصل كل منشور على متوسط 0 مشاهدة. وخلال اليوم الأول يجمع عادةً 0 مشاهدة.
- التفاعلات والاستجابة: يتفاعل الجمهور بانتظام؛ متوسط التفاعلات لكل منشور يبلغ 0.
- الاهتمامات الموضوعية: يركز المحتوى على مواضيع رئيسية مثل redis, hashmap, linkedhashmap, индекс, фича.
📝 الوصف وسياسة المحتوى
يصف المؤلف القناة بأنها مساحة للتعبير عن الآراء الذاتية:
“Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк
🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt
Комплименты, вопросы, предложения: @utki_letyat”
بفضل وتيرة التحديث المرتفعة (أحدث البيانات بتاريخ 09 يونيو, 2026) تحافظ القناة على حداثتها ومستوى وصول مرتفع. وتُظهر التحليلات تفاعلاً نشطاً من الجمهور، ما يجعلها نقطة تأثير مهمة ضمن فئة التكنولوجيات والتطبيقات.
// givenИнициализация переменных и моков.
// whenВызов проверяемого метода.
// thenПроверка результата и состояния других объектов. 3 блока должны чётко следовать друг за другом, тогда тест легко читать. Если после блока проверок идёт действие и дополнительные проверки — значит тест слишком большой. 2️⃣ Что такое TDD? Test Driven Development - процесс разработки, когда сначала пишется тест, а потом код. Подробно это выглядит так: ▫️Написать тест для кода, которого нет ▫️Запустить тесты, новый тест падает ▫️Написать код, чтобы тест проходил ▫️Запустить тесты, тесты проходят ▫️Рефакторинг нового кода ▫️Запустить тесты, тесты проходят Плюсы TDD: ➕Код сразу покрыт тестами ➕У класса удобный интерфейс, который сразу используется в тестах ➕Структурированный код После этого следует такой диалог: - А Вы лично используете TDD? - Нет. - Почему? У него же столько плюсов. - Большой минус - частое переключение контекста и фокус на деталях. Мне удобнее написать простое рабочее решение, и только потом добавить проверки, валидацию и обработку ошибок. После этого в соответствии с требованиями как следует оттестировать то, что получилось. Я сразу пишу код, который легко использовать и тестировать, поэтому TDD меня только замедляет. - Согласен.
class Программист {
void написатьКод()
void помытьПосуду()
void погладитьКота()
}
Идеальная сущность с точки зрения Cohesion - лямбда-выражение. Их легко читать, тестировать и переиспользовать.
2️⃣ Связанность (Coupling)
Показывает, как одна сущность взаимодействует с другими. Класс А связан с классом Б, если в классе А
▫️Поля класса Б
▫️Вызов методов класса Б
▫️Локальные переменные класса Б
▫️Если класс А - подкласс Б
В большинстве проектов есть модуль base или common - часто он является примером сильной связанности. Менять такой модуль - всегда большой риск.
3️⃣ Сложность (Complexity)
На этот показатель влияет:
▪️Сколько сущностей взаимодействуют
▪️Количество операций в методе
▪️Глубина наследования
▪️Количество ветвлений и возможных состояний
Показатель сложности часто идёт рука об руку с размером, но не всегда. Сложный метод может быть небольшим по размеру, но скрывать внутри себя десятки вариантов развития событий:
public void check(long id) {
dao.get(id).findProcessor()
.getAccount().checkBalance();
}
4️⃣ Размер (Size)
Большие сущности сложно поддерживать. В этой категории много метрик, например, количество:
🔹Полей
🔹Методов
🔹Статических методов
🔹Подклассов
🔹Связанных библиотек
А теперь ответы на вопросы перед постом:
✅ Если класс А часто использует класс Б, то это признак высокой связанности. Связанность (coupling) характеризует внешние отношения между сущностями. Связность (cohesion) - про внутреннюю структуру самой сущности.
✅ На сложность влияет количество ветвлений и глубина наследования. Остальные характеристики относятся к категории "размер".// TODO добавить тестыТакие комментариии будут отображаться в окне TODO внизу экрана. Через него можно перейти в нужное место кода в один клик. Открыть список можно через ▫️Alt+6 ▫️View→Tool Windows→TODO Помимо стандартных TODO и FIXME можно добавить свои метки, например, OPTIMIZE, ASK, TEST. Сделать это можно в File →Settings→ Editor→ TODO.
Runnable r=() -> …
❌ Runnable r=new Runnable{
@Override
public void run(){…}
}
2️⃣ Определите сценарии использования ФИ.
ФИ — это интерфейс, но с одним абстрактным методом. В него можно добавить сколько угодно методов с реализацией — дефолтных, приватных и статических. Один ФИ может расширять другой ФИ. Класс может реализовать ФИ через implements.
Чтобы код был простым и понятным, нужно разграничивать его компоненты:
🔸Цель обычного интерфейса - обозначить набор методов для будущих классов.
🔸Цель ФИ — передать набор инструкций через лямбды и ссылки на методы.
Один метод в ФИ - оптимальный вариант. Но есть случаи, когда дополнительные методы уместны, так как тесно связаны с самим интерфейсом.
Пример: интерфейс Predicate. Несмотря на то, что он функциональный, в нём есть 4 метода с заданной реализацией: and, or, isEqual и negate.
3️⃣ Используйте аннотацию FunctionalInterface.
Она не обязательна, но облегчает чтение кода.
4️⃣ Если используете ФИ как аргумент, отражайте тип ФИ в названии метода.
Пример: в классе 2 метода с одним именем и разными типами аргументов:
void adapt(Callable) void adapt(Supplier)Этот код компилируется, но у него 2 недостатка: ▪️Чтобы понять разницу между методами нужно читать код или документацию. ▪️В качестве аргумента нельзя передать лямбда-выражение, т.к оно подходит под оба типа:
❌ adapt(str->print(str))Чтобы избежать этих проблем, используйте разные имена для методов. Хороший пример — класс CompletableFuture для многоэтапных вычислений. В его методах легко ориентироваться благодаря удачным названиям:
▫️thenAccept(Consumer)
▫️thenApply(Function)
▫️thenRun(Runnable)@FunctionalInterface
public interface Archiver{
void archive(String file)
throws FileNotFoundEx;
}
Экземплярами ФИ могут быть:
🔸Лямбда-выражение
🔸Ссылка на метод
🔸Ссылка на конструктор
Для большинства задач подойдут стандартные интерфейсы из пакета java.util.function. Всего их 43, но можно выделить 5 групп:
1️⃣ Consumer (потребитель)
Метод принимает один параметр и ничего не возвращает.
Consumer<String> c = System.out::println;2️⃣ Supplier (поставщик) Ничего не принимает, но возвращает значение.
Supplier<List> s = ArrayList::new;3️⃣ Function Принимает один параметр, возвращает одно значение:
Function<String,Long> map = s -> Long.valueOf(s);4️⃣ UnaryOperator Метод принимает один параметр и возвращает значение того же типа:
UnaryOperator<Integer> inc = i -> i+1;5️⃣ Predicate Принимает аргумент и возвращает boolean значение.
Predicate<String> filter =
s -> !s.isEmpty()
&& !s.contains("q");
У этих ФИ есть удобные вариации:
🔹BiConsumer, BiFunction, BiPredicate, BinaryOperator принимают два аргумента:
BiConsumer<Integer,Integer> b = (x,y) -> println(x+y);🔹LongFunction, LongSupplier , ToLongFunction и т.д позволяют не прописывать типы аргументов или выходных значений:
LongFunction<String> to = Long::toString;Ответ на вопрос перед постом: Интерфейс становится функциональным если в нём есть один абстрактный метод. Все остальные свойства у него такие же, как у обычного интерфейса. Аннотация FunctionalInterface не обязательна, но желательна для понимания кода.
interface Adapter {
default int get() {…}
}
Класс, который реализует интерфейс, переопределяет такой метод при необходимости.
❓Зачем нужны такие методы?
1️⃣ Облегчить изменения API
Во времена java 7 интерфейсы содержали только определения методов:
interface Collection<Т> {
void add(Т);
Т get();
}
Чтобы добавить, удалить или поменять сигнатуру метода нужно одновременно поменять код со стороны пользователей интерфейса и во всех реализациях. В рамках одного проекта это несложно, но для библиотек это сложная задача. Пользователи могут столкнутся с проблемами совместимости при переходе на новую версию.
Задача дефолтных методов - сгладить этот процесс и предоставить приемлемую или временную альтернативу.
2️⃣ Вспомогательные методы
Те методы, которые не входят в прямую функциональность интерфейса - методы для мониторинга, логгирования и т.д. Используйте с осторожностью, этот приём может нарушать принцип Interface segregation.
3️⃣ Комбинации базовых методов
В интерфейсе объявляется минимально необходимый набор методов, а некоторые методы являются просто их комбинациями. Мы обсуждали этот случай в прошлом посте. Такой "удобный" метод можно добавить в интерфейс как статический.
Статические методы вызываются через имя интерфейса и недоступны у экземпляров. Если это неудобно, оформите метод как метод по умолчанию.
Пример: интерфейс Comparator.
Основная функция — сравнение объектов через метод compare, индивидуальный для каждого класса. Цепочка из 2 компараторов - это 2 вызова compare и объединение результатов. Логика всегда одинакова, и нет смысла дублировать её в каждом подклассе:
default Comparator<T> thenComparing(Comparator) {…};
❓Что если класс реализует 2 интерфейса с методами по умолчанию?
Для разрешения конфликта используются 2 правила:
1️⃣ Если в классе переопределён метод по умолчанию — используется метод класса.
2️⃣ В иерархии интерфейсов используется метод наследника:
interface Child extends ParentЕсли класс реализует интерфейс Child, то будет использоваться дефолтный метод интерфейса Child. В опросе перед постом ни одно правило не подходит, поэтому будет ошибка компиляции. Чтобы разрешить конфликт, класс должен явно реализовать метод get();
متاح الآن! بحث تيليغرام 2025 — أهم رؤى العام 
