Как запустить код после конструктора в сборщике Lombok

У меня есть класс, который я хочу использовать Lombok.Builder, и мне нужна предварительная обработка некоторых параметров. Что-то вроде этого:

@Builder
public class Foo {
   public String val1;
   public int val2;
   public List<String> listValues;

   public void init(){
       // do some checks with the values.
   }
}

обычно я бы просто позвонилinit() на конструкторе NoArg, но с сгенерированным конструктором я не могу это сделать. Есть ли способ для этогоinit быть вызванным сгенерированным строителем? Напримерbuild() будет генерировать код вроде:

public Foo build() {
   Foo foo = Foo(params....)
   foo.init();
   return foo;
}

Я знаю, что могу вручную кодироватьall args конструктор, что строитель будет звонить через него, и я могу позвонитьinit внутри

Но это неоптимальное решение, так как мой класс, вероятно, будет время от времени добавлять новые поля, что также означало бы изменение конструктора.

Ответы на вопрос(4)

Foo ы можете вручную добавить конструктор, который выполняет инициализацию, и положить@Builder на конструкторе. Я знаю, что вы уже знаете это, но я думаю, что это правильное решение, и вы не забудете добавить параметр, так как вы все равно хотите использовать код в сборщике.

Раскрытие информации: я разработчик ломбок.

 Budius23 июн. 2016 г., 10:23
привет Роэль, я рад, что ты зашел, чтобы ответить. Спасибо за освобождение. Если я создаю свой собственный конструктор, это означает, что мне придется вручную создавать конструктор "all-arg" и поддерживать его каждый раз, когда появляются новые поля. В поисках больше я нашел этот запрос на функцию в библиотекеgithub.com/rzwitserloot/lombok/issues/674 и я думаю, что это действительно то, что я хотел.

расширил генератор сгенерированных и вызвалinit() себя.

Пример:

@Builder(toBuilder = true, builderClassName = "FooInternalBuilder", builderMethodName = "internalBuilder")
public class Foo {

   public String val1;
   public int val2;
   @Singular public List<String> listValues;

   void init() {
      // perform values initialisation
   }

   public static Builder builder() {
      return new Builder();
   }

   public static class Builder extends FooInternalBuilder {

      Builder() {
         super();
      }

      @Override public Foo build() {
         Foo foo = super.build();
         foo.init();
         return foo;
      }
   }
}
 Pawel31 окт. 2016 г., 18:08
Круто, спасибо, что поделились :-)
 Budius23 июн. 2016 г., 10:23
Это интересно. Я сделаю тест и проверим это.
 Roel Spilker23 июн. 2016 г., 10:23
Вам не нужно предоставлятьbuilderClassname, Если у вас есть классFooBuilder в вашем коде lombok просто добавит методы к этому классу.
 Soumen19 окт. 2016 г., 21:35
Действительно интересная реализация. Благодарю. Так как вся идея состоит в том, чтобы дополнить конструктор, разве init () не должен быть определен как часть Builder?
 Budius22 июн. 2016 г., 15:49
проблема с этим подходом состоит в том, что он не очень хорошо работает сtoBuilder() но остальное в порядке.

я хотел добавить методbuildOptional() застройщику не повторитьOptional.of(Foo) каждый раз, когда мне это нужно. Это не сработало с ранее опубликованным подходом, потому что цепочечные методы возвращаютFooInternalBuilder объекты; и положитьbuildOptional() вFooInternalBuilder пропуститinit() выполнение метода вBuilder...

Также лично мне не понравилось наличие 2 классов строителей.

Вот что я сделал вместо этого:

@Builder(buildMethodName = "buildInternal")
@ToString
public class Foo {
    public String val1;
    public int val2;
    @Singular  public List<String> listValues;

    public void init(){
        // do some checks with the values.
    }    

    /** Add some functionality to the generated builder class */
    public static class FooBuilder {
        public Optional<Foo> buildOptional() {
            return Optional.of(this.build());
        }

        public Foo build() {
            Foo foo = this.buildInternal();
            foo.init();
            return foo;
        }
    }
}

Вы можете сделать быстрый тест с помощью этого основного метода:

public static void main(String[] args) {
    Foo foo = Foo.builder().val1("String").val2(14)
            .listValue("1").listValue("2").build();
    System.out.println(foo);

    Optional<Foo> fooOpt = Foo.builder().val1("String").val2(14)
            .listValue("1").listValue("2").buildOptional();
    System.out.println(fooOpt);
}

Сделав так, давайте добавим то, что я хочу:

Добавитьinit() метод, который выполняется после каждой постройки объекта автоматическиДобавление новых полей не требует дополнительной работы (как это было бы для индивидуально написанного конструктора)Возможность добавления дополнительных функций (в том числеinit() исполнение)Сохраните полную стандартную функциональность@Builder аннотация приноситНе подвергайте дополнительный класс строителя

Даже если вы решили свою проблему, прежде чем я хотел бы поделиться этим в качестве решения. Это немного короче и добавляет (для меня) приятную особенность.

Это работает для меня, не полное решение, но быстро и легко.

@Builder
@AllArgsConstructor
public class Foo {
   @Builder.Default
   int bar = 42;
   Foo init() {
      // perform values initialisation
     bar = 451;   // replaces 314
     return foo;
   }
   static Foo test() {
       return new FooBuilder()  // defaults to 42
           .bar(314)  // replaces 42 with 314
           .build()
           .init();   // replaces 314 with 451
   }
}

Ваш ответ на вопрос