Создайте объект из существующего с помощью ломбока

100

Допустим, у меня есть аннотированный класс ломбока, например

@Builder
class Band {
   String name;
   String type;
}

Я знаю, что могу:

Band rollingStones = Band.builder().name("Rolling Stones").type("Rock Band").build();

Есть ли простой способ создать объект Foo, используя существующий объект в качестве шаблона и изменив одно из его свойств?

Что-то типа:

Band nirvana = Band.builder(rollingStones).name("Nirvana");

Я не могу найти это в документации по ломбоку.

Мустафа
источник

Ответы:

223

Вы можете использовать toBuilderпараметр, чтобы дать своим экземплярам toBuilder()метод.

@Builder(toBuilder=true)
class Foo {
   int x;
   ...
}

Foo f0 = Foo.builder().build();
Foo f1 = f0.toBuilder().x(42).build();

Из документации :

Если вы используете @Builder для генерации построителей для создания экземпляров вашего собственного класса (это всегда так, если не добавляете @Builder к методу, который не возвращает ваш собственный тип), вы можете использовать @Builder (toBuilder = true), чтобы также генерировать метод экземпляра в вашем классе, называемый toBuilder (); он создает новый конструктор, который начинает со всеми значениями этого экземпляра.

Отказ от ответственности: я разработчик ломбока.

Рул Спилкер
источник
11
@Mustafa Там также @Wither, что является более эффективным для изменения отдельных полей: Foo f1 = f0.withX(42).
maaartinus
@maaartinus Я думаю, @Witherгенерируется с помощью * методов, которые всегда возвращают новый объект, вместо того, чтобы устанавливать поле существующего объекта. Это низкая эффективность.
MGhostSoft
2
@MGhostSoft Я, очевидно, предполагаю, что целью является создание нового объекта. Таким образом, мы довольно распространены, поскольку неизменяемые объекты используются все больше и больше. ++ × @WitherЛучше всего для изменения одного поля . Если больше двух, toBuilderпобеда. Смотрите мой ответ ниже.
maaartinus
2
А для нулевых полей (т.е. копии объекта без каких-либо изменений) @Witherвообще не будет работать, но .toBuilder().build()будет.
М. Джастин
38

Есть ли простой способ создать объект Foo, используя существующий объект в качестве шаблона и изменив одно из его свойств? ( курсив мой )

Если вы действительно хотите изменить одно свойство, есть более приятный и эффективный способ:

@With
class Band {
   String name;
   String type;
}

Band nirvana = rollingStones.withName("Nirvana");

Холка не создает мусора, но может изменить только одно поле. Для изменения многих полей вы можете использовать

withA(a).withB(b).withC(c)....

и производить тонны мусора (все промежуточные результаты), но чем toBuilderэффективнее и естественнее.

ПРИМЕЧАНИЕ. В более старых версиях ломбока использовались @Witherаннотации. См. Начало документации .

Maaartinus
источник
1
неужели это создаст столько мусора? Я думаю, что это все мелкие копии, за исключением поля, которое вы заменяете (полагаясь на то, что вы уже сделаете объект неизменным, если это то, что было задумано). «Мусор» будет в основном отброшенными ссылками на объекты верхнего уровня (хотя я предполагаю, что многие примитивы также могут привести к большему количеству мусора).
jm0
1
@ jm0 Конечно, это все мелкие копии. Под «тоннами мусора» я имел в виду n-1объекты для серии nобращений withSomething. Объект стоит примерно несколько байтов плюс 4 или 8 байтов на ссылку плюс от 1 до 8 байтов на примитив. Итак, мы говорим о десятках байтов на вызов. Обычно ничего страшного.
maaartinus