Одна из самых главных плюшек в Груви, это, конечно метапрограммирование. Оно бывает двух типов — во время компиляции и во время исполнения.
Метапрограммирование во время исполнения это примерно «ах, вы вызвали метод, которого не существует? Не страшно, мы сейчас чего-нить придумаем, на основе того, что вы имели ввиду, когда вызывали этот метод». Примеров такого масса — практически любая библиотека Груви основана на таких штуках, будь то билдеры, сларперы, Грейлзы, Рэтпак, Грейдл, и все остальное. Но, сейчас не об этом (если хотите об этом, смотрите пункт 1 наглого пиара в конце поста).
Сейчас мы поговорим о метапрограммировании во время компиляции, а именно о том, как просто написав одно в коде, получить другое (ну, или дополнительное) в байткоде.
Начнем мы с трансформации, которая прошита прямо в самом Груви, без всяких аннотаций и прочих добавок.
Пишем:
class Person {
String name
}
На выходе получаем байткод, в котором прописаны все Джававские геттеры и сеттеры (ну, в этом случае только
getName()
и setName(String name)
, но идея ясна).Эта прекрасная мелочь является полноценным примером метапрограммирования во время компиляции.
Посмотрев на это небольшое избавление от бойлерплейта, чудесный человек Danno Ferrin (@shemnon) сказал себе: «Но ведь есть еще много бойлерплейта, кроме геттеров и сеттеров, и не у всех они одинаковые! Давайте-ка придумаем чего-нить подключаемое и расширяемое!» И так родились AST Transformations (первая, как ни странно, была @Bindable
. Хотя, если посмотреть, сколько кода она выкидывает, может и не странно).
AST transformations это набор аннотаций, которые меняют абстрактное синтаксическое дерево налету во время компиляции Груви. Можно сказать, что добавление геттеров и сеттеров это встроенная трансформация AST, которая работает всегда, без добавления аннотаций. Остальные-же включаются только по требованию.
Давайте посмотрим что у нас есть:
- Итак, аннотация-пионер
@Bindable
и её подруга@Vetoable
превращают геттеры и сеттеры в настоящие properties, с возможностью нацеплять на них listener-ы и слушать, регаривать и запрещать изменения - Очень модные нынче словечки
@Category
и@Mixin
добавляют природу одного класса в другой класс. Ну, примеси! @Delegate
добавляет все методы, которые существуют у делегата, имплементируя их, натурально, делегацией, да?@Immutable
делает класс неизменяемым, а конкретно: 1. сеттеры кидаютReadOnlyPropertyException
, 2. делает классfinal
, 3. делает поляprivate
иfinal
4. добавляет конструктор со всеми полями, как просто в параметрах, так и в мапе, 4. на лету создает дефенсивные копии для изменяемых компонентов, прописываетequals
,hashcode
иtoString
- Вот еще пачка подобных борцов с бойлерплейтом:
@InheritConstructors
добавляет все конструкторы из супер-класса,@AutoClone
и@AutoExternalize
добавляет соответствующие методы, а@Canonical
делает «правильный Джава класс» — с конструктором без параметров, конструкторами, которые все параметры принимают (как подряд, так и мапой), иequals
,hashCode
иtoString
-ом. Ну, как@Immutable
, только mutable. - Еще один модный термин
@Lazy
создаст лениво-инициализруемое поле (по первому требованию), опционально, обвернутое в soft-reference @Newify
позволяет создавать объекты с помощью методаnew
вместо названия конструктора (как в Руби), или, наоборот, только по названию конструктора, безnew
(как в Пайтоне). Тут, пожалуй, не помешает пример:@Newify rubyLikeNew() {
assert Integer.new(42) == 42
}
или даже@Newify([Tree, Leaf]) buildTree() {
Tree(Tree(Leaf(1), Leaf(2)), Leaf(3))
}
В последнем примере мы создаемTree
иLeaf
без использованияnew
.
- А вот вам исправление давней несправедливости: в Груви по умолчанию все поля
public
. A как сделатьpackage
? Через@PackageScope
трансформацию! - Вне зависимости от того, считаете ли вы Singleton паттерном, или анти-паттерном, иногда приходится его писать. Ну, или просто поставить
@Singleton
над классом, и лениво-инициализируемый синглтон с двойной проверкой локинга готов. - Наш, #razborpoletov-ный Андрей написал чудесную, вошедшую в Груви 2.2
@Memoized
, которая запоминает результат работы метода, и если он вызывается еще раз, отдает результат немедленно (и да, параметры имеют значение) - И напоследок — аннотация-анекдот
@NotYetImplemented
— она переворачивает результаты JUnit тестов: те, которые должны падать, проходят, и наоборот.
И это еще не всё! Есть еще архи-важный
@CompileStatic
, @Field
, и целый набор аннотаций для облечения страданий по concurency, но это, всё-же в другой раз (ну, или, смотрите пункт 1 наглого пиара в конце поста).Безусловно, некоторая часть из подобных вещей достижима и другими способами, например, Ломбоком, но Груви — это намного больше, чем просто генератор байткода. AST трансформации это еще один прекрасный способ познакомится с этим мощнейшим языком, но это только начало.
P.S. Теперь, когда вы знаете о чем речь, вот вам две интересные хабра-статьи, о том как и чем писать новые AST трансформации. Об этом же смотрите ниже, в пункте 2 наглого пиара.
А теперь наглый пиар из 2 пунктов:
- Кому нужно Грувей с нуля и до достаточно продвинутого упора, айда на мои трейнинги, 17-го апреля в Москве и 15-го апреля в Казани(стучать alexbel)
- Кому расчленёнки абстрактного синтаксического дерева и написания собственных AST трансформаций, айда на >мои доклады <a href="<a href=«javapoint.ru/talks/»"> на JPoint 18-го апреля
This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий