Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Трансформации AST — 1-й шаг к тяжёлым веществам

Anna | 2.06.2014 | нет комментариев
А давайте сделаем magic с вашим Java кодом. Вот такой:

Берем это:

import groovy.transform.Canonical
import groovy.transform.TupleConstructor

@Canonical
@TupleConstructor
class Person {
    int id
    String firstName
    String lastName
    Date birthdate
}

Компилируем, и в байткоде получаем аналог вот этого:

Дьявольский бойлерпейт на Джаве на 100 с лишним строк

import java.util.Date;
import java.util.Map;

public class Person {
    private int id;
    private String firstName;
    private String lastName;
    private Date birthdate;

    //Эта штука добавлена @TupleConstructor-ом
    public Person(Map parameters){
        this.id = (int) parameters.get("id");
        this.firstName = (String) parameters.get("firstName");
        this.lastName = (String) parameters.get("lastName");
        this.birthdate = (Date) parameters.get("birthdate");
    }

    public Person(int id, String firstName, String lastName, Date birthdate) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.birthdate =birthdate;
    }

    public Person(int id, String firstName, String lastName) {
        this(id, firstName, lastName, null);
    }

    public Person(int id, String firstName) {
        this(id, firstName, null, null);
    }

    public Person(int id) {
        this(id, null, null, null);
    }

    public Person() {
        this(0, null, null, null);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Person person = (Person) o;

        if (id != person.id) return false;
        if (birthdate != null ? !birthdate.equals(person.birthdate) : person.birthdate != null) return false;
        if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null) return false;
        if (lastName != null ? !lastName.equals(person.lastName) : person.lastName != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result   (firstName != null ? firstName.hashCode() : 0);
        result = 31 * result   (lastName != null ? lastName.hashCode() : 0);
        result = 31 * result   (birthdate != null ? birthdate.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Person{"  
                "id="   id  
                ", firstName='"   firstName   '\''  
                ", lastName='"   lastName   '\''  
                ", birthdate="   birthdate  
                '}';
    }

    public int getId() {
        return this.id;
    }

    public void setId(int paramInt) {
        this.id = paramInt;
    }

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String paramString) {
        this.firstName = paramString;
    }

    public String getLastName() {
        return this.lastName;
    }

    public void setLastName(String paramString) {
        this.lastName = paramString;
    }

    public Date getBirthdate() {
        return this.birthdate;
    }

    public void setBirthdate(Date paramDate) {
        this.birthdate = paramDate;
    }
}

Ну, как-бы да, отрадно. Но ничего неповторимого, вот-же есть Lombok, не говоря теснее о способности всякого отменного IDE вначале генерить, а потом прятать всесь данный бойлерплейт.

Так для чего именно Groovy, отчего AST transformations?
В этой статье я испробую вкраце обосновать, для чего пользоваться Groovy AST transformations в Java планах, и (вновь-же вкраце) рассказать какие AST transfromations есть в Groovy сегодня. Если вы теснее знаете для чего, и хотите только «как и что», отважно листайте к «Вступление в AST transformations».

Выходит, отчего AST transformations а не Lombok?

Начнем с того, что для того Дабы пользоваться AST transformations вам не необходимо ни знать Groovy, ни писать на Groovy, ни запускать Groovy в рантайме. Трансформация происходит во время компиляции сорцов и Groovy добавляется одним jar-ом в список зависимостей. Всё.
Таким образом, AST transformations являются очаровательным методом «протащить» Groovy в ваш план: «Смотри, шеф, это ничего ужасного, это легко ещё одна библиотека для борьбы с бойлерплетом!». А потом, теснее, безусловно, шаг за шагом, тестом на Споке, за билдом на Грейдле, в вашем коде появится подлинный Groovy — динамический, фунциональный и изящный. AST transformations это только 1-й шаг.
Помимо того, AST transformations гораздо больше расширяемы, сильны и универсальны, чем Ломбок.
Последнее, но не менее значимое — AST transformations восхитительно поддерживаются в любом IDE с помощью Groovy, а не только в Эклипсе.
Фронтаpermark! String name @Delegate Date date }

Получаем готовую делегацию

import java.util.Date;

public class Event {
    private String name;
    private Date date;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getDate() {
        return this.date;
    }

    public void setDate(Date paramDate) {
        this.date = paramDate;
    }

    public boolean after(Date otherDate) {
        return date.after(otherDate);
    }

    public boolean before(Date otherDate) {
        return date.before(otherDate);
    }

    public long getTime() {
        return date.getTime();
    }

    public void setTime(long timestamp) {
        date.setTime(timestamp);
    }
}
  • @Immutable делает класс неизменяемым, а реально:
    1. сеттеры кидают ReadOnlyPropertyException
    2. класс становится final
    3. поля становятся private и final
    4. возникают конструкторы со всеми полями: как легко в параметрах, так и в мапе (как в первом примере)
    5. возникает код, тот, что создает резервные копии для изменяемых компонентов
    6. возникают equalshashcode и toString
  • Вот ещё пачка сходственных борцов с бойлерплейтом: @InheritConstructors добавляет все конструкторы из супер-класса, @TupleConstructor добавляет конструктор map, в котором ключи — наименования полей, а значения — значения (см. 1-й пример этой стати), @AutoClone и @AutoExternalize добавляет соответствующие способы, а @Canonical делает «верный Джава класс» — с конструктором без параметров, конструкторами, которые все параметры принимают (как подряд, так и мапой), и equalshashCode и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. Сравните с аналогом на Java:

    public Tree buildTree() {
        return new Tree(new Tree(new Leaf(1), new Leaf(2)), new Leaf(3));
    }
    
  • А вот вам исправление давнишней несправедливости: в Груви по умолчанию все поля public. A как сделатьpackage? Через @PackageScope трансформацию!
  • Вне зависимости от того, считаете ли вы Singleton паттерном, либо анти-паттерном, изредка доводится его писать. Ну, либо легко поставить @Singleton над классом, и неохотно-инициализируемый синглтон с двойственный проверкой локинга готов.
  • Наш, #razborpoletov-ный Андрей написал Удивительную, вошедшую в Груви 2.2 @Memoized, которая запоминает итог работы способа, и если он вызывается ещё раз, отдает итог незамедлительно (и да, параметры имеют значение)
  • И напоследок — аннотация-прикол @NotYetImplemented — она переворачивает итоги JUnit тестов: те, которые обязаны падать, проходят, и напротив. Помимо того, что это напоминает def true=false //happy debugging, эта штука пригодна для TDD — накидываем тесты для всех способов, включая те, которые ещё не прописаны, и принуждаем тесты, которые ещё не обязаны и не могут трудиться, проходить с поддержкой@NotYetImplemented. Таким образом, падения этих тестов не будут мешать нам тестировать остальные.

И это ещё не всё! Есть ещё архи-значимый @CompileStatic@Field, и целый комплект аннотаций для облечения терзаний по concurency, но это, всё-же, в иной раз (ну, либо, глядите пункт 1 наглого пиара в конце поста).

P.S. Сейчас, когда вы знаете о чем речь, вот вам две увлекательные програ-статьи, о том как и чем писать новые AST трансформации. Об этом же глядите ниже, в пункте 2 наглого пиара.

А сейчас наглый пиар из 2 пунктов:

  1. Кому необходимо Грувей с нуля и до довольно продвинутого упора, айда на мои трейнинги, 17-го апреля в Москве и 15-го апреля в Казани (стучать alexbel)
  2. Кому расчленёнки абстрактного синтаксического дерева и написания собственных AST трансформаций для борьбы с вашими собственными тараканами бойлерплейтами, айда на мои доклады на JPoint 18-го апреля и на JavaDay Kazan 16-го апреля (стучать вновь alexbel)
 Источник: programmingmaster.ru
Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB