Как стать автором
Обновить

Комментарии 39

Смотрел в свое время Lombok, но ощущение как от каких-то подпорок. Сейчас использую более натуральный Eclipse Xtend.
Тоже вариант. Вопрос выбора заключается лишь в необходимой функциональности и плате за неё. Мы обнаружили, что для наших целей Lombok убивает именно то, что нужно, и не требует смены парадигмы, стиля кодирования или привязки к инструментам. Переход на альтернативный JVM-язык — тоже решение проблемы, но это достаточно серьёзный шаг. Особенно если этот язык привязан к конкретной среде разработки, что в нашем случае недопустимо.
он как groovy? Может быть внутри джава классов? Или нет?
Xtend — это код-генератор. То есть на выходе он генерит обычные .java файлы. Кроме того в рантайме не требует никаких лишних библиотек. Интегрирован в Eclipse (syntax highlighting, подсказки, code-completion, debug), но не зависит от него.
НЛО прилетело и опубликовало эту надпись здесь
Для тех, кто не может себе позвонить использовать scala, kotlin, xtend и пр. это наверное вариант. Ну и вообще интересно, спасибо.
На Scala сейчас активно смотрим, но ввиду его сложности и необходимости менять парадигму пока остерегаемся применять где-либо, кроме тестов и служебного кода. Kotlin ещё явно не готов к продакшну. А с переходом на Xtend пришлось бы отказывать разработчикам в свободном выборе IDE и пересадить всех на Eclipse, чего мы пока что стараемся избегать.
Мы уже несколько лет успешно используем Scala, пока жалеть не приходилось. Сложность и парадигму можно перенимать постепенно. Самые большие трудности это:
1. Выработать единый стиль и понимание внутри команды (у нас маленькая команда, так что это не проблема)
2. Неидеальная поддержка IDE (но имхо вполне юзабельная, мы используем IntelliJ IDEA)
3. Сложнее найти людей (но толковый java программер довольно быстро начинает неплохо писать на scala)
Переход на groovy++ может быть вариантом.
Он со статическими типами как Ява, он позволяет всякие такие штуки, syntactic sugar, как груви, и вообще на груви легче переходить чем на скалу — у него синтакс максимально близок к Яве.
Спасибо за наводку. Вообще, от Groovy в своё время остались не очень хорошие впечатления в плане производительности, требований к памяти и скорости разработки. Возможно, Groovy++ может решить часть этих проблем, хотя пока что он производит несколько «сырое» впечатление.
Насчет производительности не знаю, у нас на груви не основной код, но в плане скорости разработки — мне очень нравится. Для меня основною плюс груви по сравнению со скалой — скорость перехода на него. Если не помнишь как писать что то в groovy-way, пишешь как писал бы на Яве и это будет вполне законный груви код.
А groovy++ — да, сыроват. Но можно писать и на обычном груви, просто не пользоваться def вместо имён классов, ну и покрывать всё юнит тестами :)
Скоро будет groovy 2.0 со статической типизацией. Я вот тогда его и буду пробовать :) Новость от вчера последняя бета
Шикарно! Спасибо! Наконец-то не надо писать геттеры-сеттеры)
Интересно. Спасибо!

А что Вы можете сказать по поводу производительности Lombook-а? влияет ли, или это просто кодогенератор?
Да, это препроцессор аннотаций, и он генерирует весь код, который в любом случае пришлось бы писать. Почти всю функциональность он реализует на этапе компиляции проекта. В рантайме библиотека lombok.jar не нужна, соответственно, никакого влияния на производительность она не оказывает.

Кажется, единственной функцией, которая требует включения библиотеки в рантайм, является перехват исключений с помощью @SneakyThrows. Но к этой функции я отнёсся с осторожностью и пока решил не применять в продакшне. Хотя я не ожидаю каких-либо проблем с производительностью и в этом случае.
Отлично, спасибо! Пойду ковырять:)
Я сторонник прогресса, но:

1. Не всегда геттеры сводятся к простому getProperty. Да, должны сводится, но не сводятся. И тогда надо отказываться от генерации и все равно их писать.

2. Сгенерировать геттеры-сеттеры это одна комбинация горячих клавиш. Конечно, они занимают место, но есть code-folding. К hashCode() и equals() это тоже относится. Я к чему — проект не решает какой-то адской проблемы.

3. Как любая прослойка, она будет добавлять досадные баги, вылавливание которых может запросто привести к потере сэкономленного времени.

Вещь может и хорошая, но слишком много но.
1. Всё верно, но Lombok позволяет указать геттеры и сеттеры отдельно для каждого поля, а если в аксессоре требуется какая-то дополнительная функциональность — мы просто убираем соответствующую аннотацию с поля и пишем этот аксессор вручную.

2. Всё так, но, как я сказал в самом начале статьи, наибольшие проблемы причиняет не генерация бойлерплейта, а его поддержание в актуальном состоянии. Например, добавили поле — нужно не забыть перегенерировать equals и hashCode, вставить его в конструктор, написать геттеры. Это всего лишь один из способов решения проблемы, которая, возможно, и не «адская», но, тем не менее, существует.

3. Я бы не сказал, что это какая-либо прослойка. Это, скорее, кодогенератор. Конечно, любая дополнительная библиотека в проекте добавляет сложность. Но большой плюс Lombok заключается в том, что он не нужен в рантайме.
Попробовал пример:

@Getter(lazy=true) private Map<String, String> map = initMap();


Но при компиляции ошибка:

.../test/LombokTest.java:[23,4] 'lazy' requires the field to be private and final.


Сделал поле final, скомпилировал, потом декомпилировал jad-ом и не увидел геттеров.

Интересно, как будет выглядеть код ленивой инициализации, сгенерированный lombok, учитывая что double checked locking является антипаттерном и в java работает только если поле volatile.
Похоже библиотека не совместима с aspectj-maven-plugin. Из-за этого плагина в классах отсутствовали сгенерированные геттеры. Отключил aspectj-maven-plugin, получил следующий код после декомпиляции:

    private final AtomicReference map = new AtomicReference();

    public Map getMap()
    {
        AtomicReference value = (AtomicReference)map.get();
        if(value == null)
            synchronized(map)
            {
                value = (AtomicReference)map.get();
                if(value == null)
                {
                    Map actualValue = initMap();
                    value = new AtomicReference(actualValue);
                    map.set(value);
                }
            }
        return (Map)value.get();
    }

Да, благодарю, с отсутствием final — это была моя ошибка. Исправил.
Разумеется, в многопоточности очень легко допустить трудноотлавливаемую ошибку. Но double-checked locking при грамотной реализации, на мой взгляд, является антипаттерном не в большей степени, чем вся система многопоточности с общей памятью. Иногда его приходится всё-таки использовать, и я бы вполне доверил его этой библиотеке, так как она учитывает общеизвестные питфоллы, как, например, упомянутый вами volatile.
Спасибо за статью! Нужно попробовать, будет не плохим дополнением к коду, который написан не на груви.

P.S. А посмотреть сгенерированный код можно только с помощью декомпиляции?
В библиотеке присутсвует утилита delombok, которая позволяет преобразовать любой исходник к тому виду, в котором он будет скомпилирован. Подробно её работа описана здесь. Например, вот так можно преобразовать одиночный файл:
java -jar lombok.jar delombok -p MyJavaFile.java

Спасибо за ответ! Быстрый гуглинг подсказал, что для этого также можно воспользоваться maven-lombok-plugin.
Что-то есть в этом неправильное — писать на джаве некорректный джава-код.
Да, возможно, вы правы. С другой стороны, когда в языке появляются стандартные средства расширения функциональности компилятора (препроцессинг аннотаций), понятие «корректного кода» несколько размывается.
А как работает логгер? Я добавил @Log4j, но после прохода delobok'ом код не изменился.
У меня вроде изменился. Я создал в качестве теста такой класс:
import lombok.extern.log4j.Log4j;
@Log4j
public class Test {
    public static void main(String... args) {
        log.info("lol");
    }
}

Затем выполнил:
java -jar lombok.jar delombok -p Test.java

Утилита вывела в консоль следующее:
error: package org.apache.log4j does not exist
error: package org.apache.log4j does not exist
// Generated by delombok at Thu Apr 19 20:27:33 YEKT 2012

public class Test {
	private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(Test.class);
	
	
	public static void main(String... args) {
		log.info("lol");
	}
}

Ошибки связаны с отсутствием log4j в classpath, но если учесть это при запуске утилиты, то проблем быть не должно.
Проверил вручную, получил такой же код. Возможно, у delombok из состава maven-lombok-plugin есть какие-то особенности, но с помощью него упорно логгер не генерируется…
А как такие вещи во время дебага разворачиваются?
Никаких особых проблем во время дебага под IntelliJ IDEA я не обнаружил. Например, вызов сгенерированного аксессора корректно «прошагивается», хотя войти внутрь метода, конечно, не получится.
В видео класс был скомпилирован как «javac -cp lombok.jar Mountain.java» при этом появились getter'ы и setter'ы на уровне bytecode'а. Как сторонняя библиотека из classpath смогла повлиять на компилятор?
В Java 6 появился так называемый Pluggable Annotation Processing API.
Он позволяет определять процессоры аннотаций, которые подключаются автоматически на этапе компиляции проекта. Достаточно включить процессор в classpath.
Пример см. здесь.
Точно так же, например, работает генератор метамодели в JPA.
Поясните пожалуйста, исходя из каких принципов ООП на каждое объявление поля приходится писать геттер и сеттер?
Приватная реализация, публичный интерфейс — это в самом общем виде принцип инкапсуляции. Для полей объекта в Java он реализуется с помощью аксессоров.
По-моему это неверное (хотя и практически общепринятое ныне) представление об инкапсуляции. Наличие большого количества геттеров-сеттеров в классе напротив скорее свидетельствует о том, что класс спроектирован не в соответствии с принципами ООП. Генерируя по сеттеру-геттеру на каждый (пусть даже закрытый) член — вы необоснованно раскрываете реализацию хранения данных классом и, по мере того как клиенты класса начинают связываются с этими сеттерами-геттерами, фиксируете её. То, что члены данных при этом остаются закрытыми, уже практически ничего не меняет.
Разумеется, использование геттеров и сеттеров должно быть разумным. Например, иногда более правильным будет генерировать аксессоры только на определённые поля. Или сгенерировать только геттеры, задавая значения полей в конструкторе. Или переписать геттеры для объектов так, чтобы они возвращали только immutable-значения. Всё это достаточно подробно описано у Блока в «Effective Java». В целом можно сказать, что аксессоры оставляют возможность для достаточно гибкого изменения реализации. Так что я не вижу в их использовании ничего принципиально неверного.
Хоть посту и месяц с небольшим, но сегодня я открыл для себя Lombok и он сделал остаток моего дня лучше!
Дописываю проект на Struts2 и понял, на сколько мне надоело в каждом классе писать аннотации писать геттеры/сеттеры — теряется концентрация на идее. А сделать поле публичным религия, что называется, не позволяет.

Вспомнил об этом замечательном проекте именно благодаря этой статье. За что автору «респект и уважуха»! :)
я хоть и не джавист. Но в .NET изначально были property, потом добавили еще всякого синтакического сахара. Возможно это облегчлило задачу для тех, кто пачками создает какие-то data classes, но в общем случае, когда свойств 10-15 примерно все тоже самое.
В общем такую штуку стоит тащить если есть очевидная выгода, например ветвистый DAL (Data Access Layer), в остальных случаях стоит наверное подумать.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории