How to exclude property from Lombok builder?

↘锁芯ラ 提交于 2019-12-03 10:22:16

Yes, you can place @Builder on a constructor or static (factory) method, containing just the fields you want.

Disclosure: I am a Lombok developer.

Alternatively, I found out that marking a field as final, static or static final instructs @Builder to ignore this field.

@Builder
public class MyClass {
   private String myField;

   private final String excludeThisField = "bar";
}

Lombok 1.16.10

I found that I was able to implement a "shell" of the static Builder class, add the method I want to hide with a private access modifier, and it is no longer accessible in the builder. Likewise I can add custom methods to the builder as well.

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

}

Create the builder in code and add a private setter for your property.

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

    public static class XYZClientWrapperBuilder {
        private XYZClientWrapperBuilder client(XYZClient client) { return this; }
    }
}

Here is my preferred solution. With that, you can create your field client at the end and have it depending on other fields that previously set by the builder.

XYZClientWrapper{
    String name;
    String domain;
    XYZClient client;

    @Builder
    public XYZClientWrapper(String name, String domain) {
        this.name=name;this.domain=domain;
        this.client=calculateClient();
    }
}

For factory static method example

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

then you can use as follows:

Car toyotaCorollaCar=Car.builder().name("Toyota").model("Corolla").build();
// You can see now that Car.builder().engine() is not available

Notice the static method of will be called whenever build() is called, so doing something like Car.builder().name("Toyota") won't actually set the value "Toyota" into name unless build() is called and then assigning logic within the constructor static method of is executed.

Also, Notice that the of method is privately accessed so that build method is the only method visible to the callers

I found one more solution You can wrap your field into initiated final wrapper or proxy. The easiest way to wrap it into AtomicReference.

@Builder
public class Example {
    private String field1;
    private String field2;
    private final AtomicReference<String> excluded = new AtomicReference<>(null);
}

You can interact with it inside by get and set methods but it won't be appeared in builder.

excluded.set("Some value");
excluded.get();
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!