Может ли с помощью ломбока конструктор required-arg сосуществовать с аннотациями @Value, @Builder и @Builder.Default?

Используя lombok (1.18.24), можно ли сгенерировать (1) конструктор, который принимает аргументы только для неинициализированных членов, а также (2) построитель? Я пробовал различные наборы аннотаций:

  1. []
@Value @Builder class Foo {
    String alpha;
    @Builder.Default String bravo = "x";
    Foo(String alpha) {this.alpha = alpha;}
    static void bar() {new Foo("y");}
}
[ERROR] Foo.java:[6,8] constructor Foo in class Foo cannot be applied to given types;
[ERROR]   required: java.lang.String
[ERROR]   found:    java.lang.String,java.lang.String
[ERROR]   reason: actual and formal argument lists differ in length
  1. [Конструктор AllArgs]
@Value @Builder @AllArgsConstructor class Foo {
    String alpha;
    @Builder.Default String bravo = "x";
    Foo(String alpha) {this.alpha = alpha;}
    static void bar() {new Foo("y");}
}
[ERROR] Foo.java:[11,47] variable bravo might not have been initialized
  1. [RequiredArgsConstructor]
@Value @Builder @RequiredArgsConstructor class Foo {
    String alpha;
    @Builder.Default String bravo = "x";
    static void bar() {new Foo("y");}
}
[ERROR] Foo.java:[10,29] constructor Foo in class Foo cannot be applied to given types;
[ERROR]   required: java.lang.String,java.lang.String
[ERROR]   found:    java.lang.String
[ERROR]   reason: actual and formal argument lists differ in length
  1. [AllArgsConstructor, RequiredArgsConstructor]
@Value @Builder @AllArgsConstructor @RequiredArgsConstructor class Foo {
    String alpha;
    @Builder.Default String bravo = "x";
    static void bar() {new Foo("y");}
}
[ERROR] constructor Foo(java.lang.String,java.lang.String) is already defined in class Foo
[ERROR] Foo.java:[8,37] constructor Foo(java.lang.String,java.lang.String) is already defined in class Foo
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
  1. Lombok создает для вас конструктор со всеми аргументами, если только вы сами не напишете конструктор, в этом случае вам нужно добавить @AllArgsConstructor. Что вы должны сделать здесь. Вы можете сделать его приватным, если хотите (@AllArgsConstructor(access = AccessLevel.PRIVATE). Он должен существовать, чтобы конструктор ломбока создавал объект. значение тоже).

  2. Проблема в вашем собственном коде - вы написали свой собственный конструктор, который не устанавливает поле bravo. В общем, плохая идея также иметь свой собственный конструктор, особенно если указанный конструктор не вызывает сгенерированный ломбоком. @Builder.Default изменяет то, что делает строитель (а именно: он будет использовать это значение "X", если вы попросите строитель build(), когда для него никогда не было установлено значение для bravo). Он удаляет это "X" по ходу дела, а @Value сделал это поле final, конечно, поэтому ваш конструктор не может установить неинициализированное (потому что билдер «забирает» "X") поле final, что является ошибкой компилятора.

  3. Та же проблема.

  4. AllArgs и RequiredArgs — это один и тот же конструктор, поэтому ломбок пытается сгенерировать их оба, что не работает.

@Value @Builder @AllArgsConstructor class Foo {
    String alpha;
    @Builder.Default String bravo = "x";
    Foo(String alpha) {this(alpha, "x");}
    static void bar() {new Foo("y");}
}

Это то, что вы ищете. Или, что более вероятно, избавьтесь от этого конструктора и напишите:

@Value @Builder @AllArgsConstructor class Foo {
    String alpha;
    @Builder.Default String bravo = "x";
    static void bar() {return builder().alpha("y").build(); }
}

Как правило, гибридизация чего-либо в программировании не является хорошей идеей - и здесь вы гибридизуете свой подход к построению. Какой-то пользовательский конструктор для одних, строители для других. Если нужно, ладно, но это дорого. Одной из затрат является путаница (похоже, это произошло здесь). Другим является код, подверженный ошибкам: ошибки, которые нелегко найти (поскольку один человек всегда использует один способ, и, следовательно, ошибка в другом способе не находится ни в одном из их путей кода, но она есть в вашем). Третий — дебаты о стиле; если есть 2 способа сделать одно и то же, любой код произвольно выбирает способ (это плохо; делает код излишне трудным для чтения, делая одно и то же разными способами, что неверно намекает на наличие значимой разницы, которую читатель либо беспокоиться о том, или вы потратите некоторое время на поиск этого, нет, на самом деле это не имеет значения, или вам нужно руководство по стилю, чтобы указать, когда использовать какой способ. руководство, которое очень сложно сделать; большинство людей, как правило, не хотят тратить время на написание собственного правила lint).

NB: Я сказал «в целом не очень хорошая идея», а не «это всегда неправильно». Есть веские причины для гибридизации, но имейте в виду, что это дорого обходится. Следовательно, бремя аргументации лежит на авторе гибридного подхода. Им нужно объяснить, что, несмотря на значительные затраты, связанные с наличием нескольких способов сделать одно и то же, это, тем не менее, того стоит. Ваш код явно является упрощенным примером для того, чтобы задать упрощенный вопрос SO (отлично!), Так что, возможно, здесь есть веская причина. Однако в вероятном сценарии, что нет - избавьтесь от этого рукописного конструктора.

Этот внутренний класс существует в классе «Тест», поэтому я в порядке с техническим долгом. Я просто ищу максимальную лаконичность. Я хочу иметь возможность вызывать как new Foo("y"), так и foo.toBuilder().alpha("y").build() (в реальном коде больше параметров). Путаница здесь связана с тем, влияет ли значение по умолчанию «x» на @Builder, @RequiredArgsConstructor и/или на рукописные конструкторы. Ломбок не может принять решение и не согласен с панелью «Структура» IntelliJ IDEA. Предлагаемые вами решения работают, но более многословны. Ничего страшного, но мне интересно, можно ли это сделать только с помощью аннотаций.

Hollis Waite 25.12.2022 04:16

Ломбок прекрасно может принять решение (ИСТОЧНИК: я написал большую часть этого кода). Возможно, плагин intellij (который поддерживается извне) не понимает этого правильно, и в этом случае зарегистрируйте ошибку (с плагином, а не с самим ломбоком). Как следует из названия @Builder.Default, это влияет на строителя. только.

rzwitserloot 25.12.2022 05:15

Вау, спасибо за вашу работу над ломбоком. Отличный инструмент. Я бы сказал, что аннотация @Builder.Default не должна влиять ни на @RequiredArgsConstructor, ни на рукописные конструкторы. В документации для первого указано, что «все неинициализированные окончательные поля получают параметр». Сбивает с толку то, что @Builder.Default делает недействительным то, что кажется встроенной инициализацией. Это дизайнерское решение вступает в игру только в том случае, если пользователи смешивают сборщики и стандартные конструкторы, поэтому, возможно, разработчики ломбоков рассматривают его как нерелевантный вариант использования. В любом случае, теперь я понимаю, как это работает. Спасибо еще раз.

Hollis Waite 25.12.2022 06:09

Это не влияет на конструкторы. Действительно сбивает с толку то, что он удаляет встроенную инициализацию; проблема в том, что выражение имеет побочные эффекты, например. foo++ где foo — статическое поле. Если оставить начальное значение нетронутым, то создание 1 объекта с помощью компоновщика теперь увеличивается в два раза.

rzwitserloot 25.12.2022 12:52

Ах, хорошее замечание о побочных эффектах. Я думаю, что статус-кво — наименее плохой вариант. Я думаю, что путаница усугубляется ошибочным поведением плагина IDE. Для этого я создал баг.

Hollis Waite 25.12.2022 15:14

Другие вопросы по теме