Ответ 1
Да, вы можете поместить @Builder в конструктор или статический (заводский) метод, содержащий только нужные поля.
Раскрытие информации: Я разработчик Lombok.
У меня есть класс, называемый "XYZClientWrapper", который имеет следующую структуру:
@Builder
XYZClientWrapper{
String name;
String domain;
XYZClient client;
}
Что я хочу, чтобы функция построения, сгенерированная для свойства XYZClient client
Поддерживает ли Lombok такой вариант использования?
Да, вы можете поместить @Builder в конструктор или статический (заводский) метод, содержащий только нужные поля.
Раскрытие информации: Я разработчик Lombok.
В качестве альтернативы я обнаружил, что маркирование поля как окончательного, статического или статического окончательного инструктирует @Builder
игнорировать это поле.
@Builder
public class MyClass {
private String myField;
private final String excludeThisField = "bar";
}
Ломбок 1.16.10
Я обнаружил, что мне удалось реализовать "оболочку" статического класса Builder, добавить метод, который я хочу скрыть с помощью модификатора частного доступа, и он больше не доступен в построителе. Кроме того, я могу добавить собственные методы для строителя.
package com.something;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import java.time.ZonedDateTime;
@Data
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MyClass{
//The builder will generate a method for this property for us.
private String anotherProperty;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "localDateTime", column = @Column(name = "some_date_local_date_time")),
@AttributeOverride(name = "zoneId", column = @Column(name = "some__date_zone_id"))
})
@Getter(AccessLevel.PRIVATE)
@Setter(AccessLevel.PRIVATE)
private ZonedDateTimeEmbeddable someDateInternal;
public ZonedDateTime getSomeDate() {
return someDateInternal.toZonedDateTime();
}
public void setSomeDate(ZonedDateTime someDate) {
someDateInternal = new ZonedDateTimeEmbeddable(someDate);
}
public static class MyClassBuilder {
//Prevent direct access to the internal private field by pre-creating builder method with private access.
private MyClassBuilder shipmentDateInternal(ZonedDateTimeEmbeddable zonedDateTimeEmbeddable) {
return this;
}
//Add a builder method because we don't have a field for this Type
public MyClassBuilder someDate(ZonedDateTime someDate) {
someDateInternal = new ZonedDateTimeEmbeddable(someDate);
return this;
}
}
}
Создайте конструктор в коде и добавьте частный сеттер для вашего свойства.
@Builder
XYZClientWrapper{
String name;
String domain;
XYZClient client;
public static class XYZClientWrapperBuilder {
private XYZClientWrapperBuilder client(XYZClient client) { return this; }
}
}
Вот мое предпочтительное решение. При этом вы можете создать своего client
в конце и иметь его в зависимости от других полей, которые ранее устанавливались разработчиком.
XYZClientWrapper{
String name;
String domain;
XYZClient client;
@Builder
public XYZClientWrapper(String name, String domain) {
this.name=name;this.domain=domain;
this.client=calculateClient();
}
}
Для заводского статического метода, пример
class Car{
private String name;
private String model;
private Engine engine; // we want to ignore setting this
@Builder
private static Car of(String name, String model){
Car car=new Car();
car.name = name;
car.model = model;
constructEngine(car); // some static private method to construct engine internally
return car;
}
private static void constructEngine(Car car) {
// car.engine = blabla...
// construct engine internally
}
}
тогда вы можете использовать следующее:
Car toyotaCorollaCar=Car.builder().name("Toyota").model("Corolla").build();
// You can see now that Car.builder().engine() is not available
Обратите внимание, статический метод of
будет вызываться всякий раз, когда вызывается build(), поэтому выполнение чего-то вроде Car.builder().name("Toyota")
фактически не установит значение "Toyota"
в name
, если не вызывается build()
и затем выполняется назначение логики в статическом методе конструктора of
.
Кроме того, обратите внимание, что метод of
доступен для частного доступа, так что метод build
является единственным методом, видимым для вызывающих
Я нашел еще одно решение Вы можете заключить свое поле в инициированную финальную оболочку или прокси. Самый простой способ обернуть его в AtomicReference.
@Builder
public class Example {
private String field1;
private String field2;
private final AtomicReference<String> excluded = new AtomicReference<>(null);
}
Вы можете взаимодействовать с ним внутри с помощью методов get и set, но он не будет отображаться в компоновщике.
excluded.set("Some value");
excluded.get();