簡體   English   中英

如何從 Lombok builder 中排除財產?

[英]How to exclude property from Lombok builder?

我有一個名為“XYZClientWrapper”的 class,它具有以下結構:

@Builder
XYZClientWrapper{
    String name;
    String domain;
    XYZClient client;
}

我不想要為屬性XYZClient client生成的構建 function

Lombok 是否支持這樣的用例?

是的,您可以將@Builder放在構造函數或靜態(工廠)方法上,只包含您想要的字段。

披露:我是龍目島的開發人員。

或者,我發現將字段標記為finalstaticstatic final指示@Builder忽略此字段。

@Builder
public class MyClass {
   private String myField;

   private final String excludeThisField = "bar";
}

龍目島 1.16.10

在代碼中創建構建器並為您的屬性添加一個私有設置器。

@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 ,只要建立()被調用將被調用,這樣做類似Car.builder().name("Toyota")將不會在值實際設置"Toyota"name ,除非build()被調用,然后執行構造函數靜態方法中of分配邏輯。

另外,請注意of方法是私有訪問的,因此build方法是調用者可見的唯一方法

我發現我能夠實現靜態 Builder 類的“shell”,添加我想用私有訪問修飾符隱藏的方法,並且它在構建器中不再可訪問。 同樣,我也可以向構建器添加自定義方法。

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;
        }
    }

}

使用 Lombok @Builder向類添加所謂的“部分構建器”會有所幫助。 訣竅是添加一個內部部分構建器類,如下所示:

@Getter
@Builder
class Human {
    private final String name;
    private final String surname;
    private final Gender gender;
    private final String prefix; // Should be hidden, depends on gender

    // Partial builder to manage dependent fields, and hidden fields
    public static class HumanBuilder {

        public HumanBuilder gender(final Gender gender) {
            this.gender = gender;
            if (Gender.MALE == gender) {
                this.prefix = "Mr.";
            } else if (Gender.FEMALE == gender) {
                this.prefix = "Ms.";
            } else {
                this.prefix = "";
            }
            return this;
        }

        // This method hides the field from external set 
        private HumanBuilder prefix(final String prefix) {
            return this;
        }

    }

}

PS:@Builder 允許更改生成的構建器類名。 上面的例子假設使用了默認的構建器類名。

我找到了另一種解決方案您可以將您的字段包裝到啟動的最終包裝器或代理中。 將其包裝到 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();

我有另一種使用@DelegateInner Class ,它支持排除字段的“計算值”。

首先,我們將要排除的字段移動到Inner Class以避免 Lombok 將它們包含在 Builder 中。

然后,我們使用@Delegate來公開構建器排除字段的 Getter/Setter。

例子:

@Builder
@Getter @Setter @ToString
class Person {

    private String name;
    private int value;
    /* ... More builder-included fields here */

    @Getter @Setter @ToString
    private class BuilderIgnored {

        private String position; // Not included in the Builder, and remain `null` until p.setPosition(...)
        private String nickname; // Lazy initialized as `name+value`, but we can use setter to set a new value
        /* ... More ignored fields here! ... */

        public String getNickname(){ // Computed value for `nickname`
            if(nickname == null){
                nickname = name+value;
            }
            return nickname;
        }
        /* ... More computed fields' getters here! ... */

    }
    @Delegate @Getter(AccessLevel.NONE) // Delegate Lombok Getters/Setters and custom Getters
    private final BuilderIgnored ignored = new BuilderIgnored();

}

positionnickname實際上是內部類的字段,對於這個Person類的外部來說是透明的。

Person p = Person.builder().name("Test").value(123).build();
System.out.println(p); // Person(name=Test, value=123, ignored=Person.BuilderIgnored(position=null, nickname=Test123))
p.setNickname("Hello World");
p.setPosition("Manager");
System.out.println(p); // Person(name=Test, value=123, ignored=Person.BuilderIgnored(position=Manager, nickname=Hello World))

優點:

  • 不要強制排除的字段是final
  • 支持排除字段的計算值
  • 允許計算字段引用構建器設置的任何字段(換句話說,允許內部類是非靜態類)
  • 不需要重復所有字段的列表(例如,在構造函數中列出除排除字段之外的所有字段)
  • 不要覆蓋 Lombok 庫的@Builder (例如,創建MyBuilder extends FooBuilder

缺點:

  • 排除的字段實際上是Inner Class字段; 但是,使用帶有適當 Getter/Setter 的private標識符,您可以將它們模擬為真實字段
  • 因此,這種方法限制您使用 Getters/Setters 訪問排除的字段
  • 計算值在調用 Getters 時被延遲初始化,而不是在.build()

我喜歡和使用的一種方法是這個。 在構造函數中保留必需參數,並通過構建器設置可選參數。 如果所需的數量不是很大,則可以使用。

class A {
    private int required1;
    private int required2;
    private int optional1;
    private int optional2;

    public A(int required1, int required2) {
        this.required1 = required1;
        this.required2 = required2;
    }

    @Builder(toBuilder = true)
    public A setOptionals(int optional1, int optional2) {
        this.optional1 = optional1;
        this.optional2 = optional2;
        return this;
    }
}

然后用

A a = new A(1, 2).builder().optional1(3).optional2(4).build();

這種方法的好處是可選值也可以有默認值。

我以前使用的一種方法是將實例字段分組為配置字段和會話字段。 配置字段作為類實例,對 Builder 可見,而 Session 字段進入嵌套的private static class ,並通過具體的final實例字段(默認情況下 Builder 將忽略)訪問。

像這樣的東西:

@Builder
class XYZClientWrapper{
    private String name;
    private String domain;
 
    private static class Session {
        XYZClient client;
    }

    private final Session session = new Session();

    private void initSession() {
        session.client = ...;
    }
 
    public void foo() {
        System.out.println("name: " + name);
        System.out.println("domain: " + domain;
        System.out.println("client: " + session.client);
    }
}

要從構建器中排除字段,請嘗試使用@Builder.Default

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM