Трансформации AST — It’s a kind of magic

от автора

А давайте я вам расскажу про полезный magic.

Одна из самых главных плюшек в Груви, это, конечно метапрограммирование. Оно бывает двух типов — во время компиляции и во время исполнения.
Метапрограммирование во время исполнения это примерно «ах, вы вызвали метод, которого не существует? Не страшно, мы сейчас чего-нить придумаем, на основе того, что вы имели ввиду, когда вызывали этот метод». Примеров такого масса — практически любая библиотека Груви основана на таких штуках, будь то билдеры, сларперы, Грейлзы, Рэтпак, Грейдл, и все остальное. Но, сейчас не об этом (если хотите об этом, смотрите пункт 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 пунктов:

  1. Кому нужно Грувей с нуля и до достаточно продвинутого упора, айда на мои трейнинги, 17-го апреля в Москве и 15-го апреля в Казани(стучать alexbel)
  2. Кому расчленёнки абстрактного синтаксического дерева и написания собственных AST трансформаций, айда на >мои доклады <a href="<a href=«javapoint.ru/talks/»"> на JPoint 18-го апреля

AST трансформации

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Никто ещё не голосовал. Воздержавшихся нет.

ссылка на оригинал статьи http://habrahabr.ru/post/215069/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *