...

суббота, 8 марта 2014 г.

Трансформации 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-го апреля




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.


Комментариев нет:

Отправить комментарий