Проект Lombok, або Оголошуємо війну бойлерплейту

Відкрию не Америку, але скриньку Пандори: у Java-коді багато бойлерплейту. Типові гетери, сеттери та конструктори, методи лінивої ініціалізації, методи toString, hashCode, equals, обробники винятків, які ніколи не викидаються, закривання потоків, блоки синхронізації. Проблема полягає навіть не в тому, щоб написати все це – сучасні середовища розробки справляються з такими завданнями натисканням кількох клавіш. Складність у підтримці бойлерплейта в актуальному стані в міру внесення модифікацій у код. А в деяких випадках (багатопоточність, реалізація методів hashCode та equals) і сам шаблонний код написати без помилок — далеко не просте завдання. Одним із рішень проблеми є генерація коду, і в цій статті я розповім про проект Lombok — бібліотеку, яка не тільки може позбавити вас бойлерплейту, але й зробити це максимально прозоро, з мінімальною конфігурацією і, що важливо, з підтримкою на рівні розробки. .

Підключаємо Lombok

Lombok використовує механізм процесингу анотацій з Java 6, з чого випливають його мінімальні вимоги до оточення. Для підключення Lombok до проекту достатньо увімкнути його в залежності. У разі використання Maven це робиться так:

Для більшої частини функціональності Lombok ця бібліотека потрібна лише на етапі компіляції. Остання версія Lombok на даний момент (0.11.0) ще не потрапила до центрального репозиторію Maven, проте її без проблем можна встановити в локальний або корпоративний репозиторій, завантаживши з сайту.

Прощаємось з аксесорами

Параметр lazy=true анотації Getter дозволяє реалізувати ліниву ініціалізацію поля.у вигляді блокування з подвійною перевіркою.

Деструкція конструкторів

Конструктори POJO-класів теж не відрізняються складністю та різноманітністю — найчастіше нам необхідно щось із цього списку: конструктор без параметрів, конструктор з усіма параметрами, конструктор тільки з деякими обов'язковими параметрами, статичний factory-метод. Lombok легко справляється з цим завданням за допомогою анотацій @NoArgsConstructor, @AllArgsConstructor, @RequiredArgsConstructor і параметра staticName відповідно.

Ось що ми отримаємо в результаті:

Генеруємо типові методи: toString, hashCode, equals

Про правильну реалізацію методів equals та hashCode написано досить багато — мабуть, варто з цього приводу згадати «Effective Java» Блоку та статтю Одерські. Коротко можна сказати, що реалізувати їх коректно — непросто, підтримувати в актуальному стані ще складніше, а займати вони цілком можуть добру половину класу. Метод toString не такий критичний для коректності коду, але актуалізувати його щоразу при зміні класу теж приємного мало. Надаємо можливість Lombok зробити за нас цю невдячну роботу за допомогою двох нехитрих анотацій:

Логгер-невидимка

Замість цього Lombok пропонує скористатися анотаціями @Log, @CommonsLog, @Log4j або @Slf4j — залежно від засобу протоколювання, що віддається переваги:

Фіналізуємо локальні змінні

Змінна map у разі буде оголошено як final, до того ж час опис її типу взято з правої частини висловлювання присвоювання.

Безкарно кидаємо винятки

Не всі розробники, на жаль, читали вже згадувану тут «Effective Java» Блоку чи «Robust Java» Стелтинга. А може читали, але не дуже уважно. Або,можливо, у них і справді була якась цілком обґрунтована мотивація оголосити ось цей конкретний виняток перевіреним — але вам від цього не легше, адже ви знаєте, що воно не виникне ніколи! Що робити, наприклад, з UnsupportedEncodingException, якщо ви абсолютно впевнені, що без системної підтримки кодування UTF-8 ваш додаток все одно не працюватиме? Доводиться укладати код у try-catch і писати безглуздий висновок у лог, який ніколи не дочекається свого часу, переобертати виняток у runtime-обертку, якій не судилося з'явитися на світ, або зовсім ігнорувати порожнім блоком перехоплення (який, не знаю, як у вас, а в мене особисто завжди викликає бажання схопитися за револьвер). Lombok і тут пропонує альтернативу.

Для цього чаклунства, на відміну від усього іншого, доведеться підключити Lombok в рантаймі. Все просто: Lombok присипляє пильність компілятора, перехоплюючи виняток у try, а потім у рантаймі непомітно перекидає його в catch. Фішка в тому, що на рівні байт-коду можна викинути будь-який виняток, навіть той, який не оголошений у сигнатурі методу.

Правильна синхронізація

Багатопотоковість - дуже складна область програмування, що має в Java свою ідіоматику та патерни. Однією з правильних практик є використання для синхронізації приватних фінальних полів, тому що на будь-якому загальнодоступному лоці може побажати синхронізуватися якийсь не пов'язаний шматок функціональності, що призведе до непотрібних блокувань і помилок, що важко відловлюються. Lombok вміє коректно синхронізувати вміст методу, позначеного інструкцією @Synchronized - як статичного, так і методу екземпляра:

Ось що отримаємо:

А що на це скаже IDE?

Всі ці чудові фішки нічого не коштували б, якщоПри відкритті проекту в Eclipse або IntelliJ IDEA рядки коду розгорялися б червоним полум'ям від праведного гніву компілятора. На щастя, інтеграція з середовищами розробки є, і досить хороша. Для IntelliJ IDEA плагін присутній у стандартному репозиторії:

бойлерплейту

Для Eclipse та NetBeans установка трохи незвичайна. Необхідно запустити файл lombok.jar, і він покаже симпатичний інсталятор, який пропонує накотити Lombok на існуючі інсталяції Eclipse:

lombok

Зазначені плагіни не тільки переконують середовище розробки в тому, що відсутність геттерів, сеттерів та інших елементів бойлерплейту їй тільки здається - вони ще й коректно підсвічують помилкові ситуації, наприклад, коли геттер, якому належить бути згенерованим, вже є в класі:

Я перерахував основні фішки Lombok, але це ще не все. Більш детально всі можливі анотації з усіма атрибутами та порівнянням коду «до» і «після» описані в документації.