How to generate getters for lombok Builder? - java

I have the following definition:
#Value
#Builder(toBuilder = true)
public class MyEntity {
String name;
}
When trying to use a getter on a builder, e.g. MyEntityBuilder.getName(), IDEA states that it "Cannot resolve method". Also, IDEA doesn't auto-complete it.
The only available method (except build()) is the setter: MyEntityBuilder name(String name);
Is there a way to generate getter on lombok generated builders? Thanks.
Using Lombok 1.8.16.

There seems to be no option to do it out of the box according to the Lombok docs or the source code.
If you want to inspect the contents of the builder, the best you can do, it seems, is to build the object and inspect the field from there.

You mentioned you were using IntelliJ IDEA as your IDE. It so happens that there is an IDEA plugin exactly for this:
https://plugins.jetbrains.com/plugin/6317-lombok
Once the plugin is installed, IDEA should recognize Lombok's annotation syntax.

Builders do not,
generally,
have getters.
The builder pattern is this:
Create the builder.
Set values in the builder.
Call build().

As answer from DwB suggests builder pattern does not need nor use getters.
You can have a getter for Lombok builder values but I think that it would not be very useful. You can customize builder this way:
#Value
#Builder(toBuilder = true)
public class MyEntity {
String name;
String name2; // just something to set also
// Customized builder
public static class MyEntityBuilder {
private String name;
public String getName() {
return this.name;
}
// This is not actually needed but just as an example howto
// customise a setter.
public MyEntityBuilder name(String name) {
this.name = name;
return this;
}
}
}
And to test it (Junit5):
#Test
void test() {
MyEntityBuilder meb = MyEntity.builder();
var myEntity = meb
// You need to set this first to access it later
.name("Name #1")
// The benefit having a getter ?
.name2(meb.getName())
.build();
assertEquals(myEntity.getName(), myEntity.getName2());
}

Related

Generate MapStruct presence checker methods with Lombok

MapStruct is aware of source presence checking
and uses presence checker methods by default (if present of course) to verify if a field in a target object should be updated with a value in a source
object. Without presence checkers MapStruct by default updates only fields with non-null values.
I want to use DTO in a REST controller to implement partial update strategy using MapStruct's source presence checknig but since I use Lombok to generate getters and setters I also want to generate source presence checking methods.
There isn't any way to do that. SOURCE: Me. I'm a core lombok contributor.
However, there is a recent, and very long, feature request discussion on this very idea: Issue #2669: Generate hasXXX() method. It goes in multiple wrong directions, so I would suggest you start from the end, where some concrete plans on how to get there from here are listed. It's complicated.
In short I created a lombok extension which supports #PresenceChecker annotation and generates hasXXX() methods.
// Original code
#PresenceChecker
#Getter
#Setter
public class UserUpdateDto {
private String name;
}
//Generated code
public class UserUpdateDto {
private boolean hasName;
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
this.hasName = true;
}
public boolean hasName() {
return this.hasName;
}
}
More detailed answer can be found here

Lombok #Builder with Java 8 Lambda Builder Pattern

I am trying to use Lombok with the Java 8 Lambda builder pattern introduced here.
POJO:
#JsonInclude(Include.NON_NULL)
#Builder
#NoArgsConstructor
#RequiredArgsConstructor
#AllArgsConstructor
public class RestResponse<T> {
#Getter #Setter #Builder.Default private Boolean success = true;
#Getter #Setter #NonNull private T data;
public static class RestResponseBuilder<T> {
public RestResponseBuilder<T> with(Consumer<RestResponseBuilder<T>> builderFunction) {
builderFunction.accept(this);
return this;
}
public RestResponse<T> createRestResponse() {
return new RestResponse<T>(success, data);
}
}
}
Usage:
#GetMapping(value = "/testLambdaBuilder", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public RestResponse<String> testEndpointLambdaBuilder() {
return new RestResponseBuilder<String>().with($ -> $.data = "helloWorld").createRestResponse();
}
Lombok seems to create a package level constructor for the builder. Is there a way to change it to public? The error I'm getting is:
The constructor RestResponse.RestResponseBuilder() is not visible
Because the builder class already partially exists, Lombok will simply inject the correct fields into your RestResponseBuilder. From the documentation (emphasis mine):
Each listed generated element will be silently skipped if that element
already exists (disregarding parameter counts and looking only at
names). This includes the builder itself: If that class already
exists, lombok will simply start injecting fields and methods inside
this already existing class, unless of course the fields / methods to
be injected already exist.
So if you want the field to have public visibility in the builder class then you just need to declare it there and Lombok will respect it:
public static class RestResponseBuilder<T> {
public T data;
public RestResponseBuilder<T> with(Consumer<RestResponseBuilder<T>> builderFunction) {
builderFunction.accept(this);
return this;
}
public RestResponse<T> createRestResponse() {
return new RestResponse<T>(success, data);
}
}
All of this said, your with method seems quite strange. You can just do this:
return new RestResponse.RestResponseBuilder<String>().data("helloWorld").createRestResponse();
What's the point in using the lambda builder pattern in combination with Lombok?
As I understand it, using the lambda builder pattern saves you from adding the setter methods in your builder (and implementing it correctly) every time you add a new variable to your pojo / value object.
But if you already use Lombok to generate your builder, all of the builder code is already generated. So there is no need to write the methods yourself. Lombok also updates the builder code whenever you change your pojo.
So I would recommend: either use Lombok and go with the default builder that gets generated OR write your builder yourself and use the lambda builder pattern to save you from writing and maintaining too many methods.

Is it possible to make Lombok's builder public?

I am using Lombok library in my project and I am not able to use a class annotated with #Builder in outer packages.
Is there a way to make the builder public?
MyClass instance = new MyClass.MyClassBuilder().build();
The error is:
'MyClassBuilder()' is not public in
'com.foo.MyClass.MyClassBuilder'. Cannot be accessed
from outside package
#Builder already produces public methods, it's just the constructor that's package-private. The reason is that they intend for you to use the static builder() method, which is public, instead of using the constructor directly:
Foo foo = Foo.builder()
.property("hello, world")
.build();
If you really, really, really want the constructor to be public (there seems to be some suggestion that other reflection-based libraries might require it), then Lombok will never override anything that you've already declared explicitly, so you can declare a skeleton like this with a public constructor and Lombok will fill in the rest, without changing the constructor to package-private or anything.
#Builder
public class Foo
{
private final String property;
public static class FooBuilder
{
public FooBuilder() { }
// Lombok will fill in the fields and methods
}
}
This general strategy of allowing partial implementations to override default behaviour applies to most (maybe all) other Lombok annotations too. If your class is annotated with #ToString but you already declared a toString method, it will leave yours alone.
Just to show you everything that gets generated, I wrote the following class:
#Builder
public class Foo
{
private final String property;
}
I then ran it through delombok to see everything that was generated. As you can see, everything is public:
public class Foo
{
private final String property;
#java.beans.ConstructorProperties({"property"})
Foo(final String property) {
this.property = property;
}
public static FooBuilder builder() {
return new FooBuilder();
}
public static class FooBuilder
{
private String property;
FooBuilder() { }
public FooBuilder property(final String property) {
this.property = property;
return this;
}
public Foo build() {
return new Foo(property);
}
public String toString() {
return "Foo.FooBuilder(property=" + this.property + ")";
}
}
}
The problem is you are using #Builder in the wrong way.
When Builder Pattern is used, you only need to use the static method to invoke it and then build, for example:
MyClass instance = MyClass.builder().build(); .
Please do not new the MyClassBuilder again, it breaks the encapsulation the pattern has since you are directly using the inner MyClassBuilder class. This constructor is been hided from outside, that's why you get the not accessible error. Instead it provides you the static method builder().
I have found this neat workaround:
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
#Getter
#Setter
#Builder
public class Customer {
private String id;
private String name;
public static MessageBuilder builder() {return new CustomerBuilder();}
}
The problem with this builder annotation is that, if you delombok you'll see, the generated constructor for the builder has no access indicator (public, private, protected) therefore is only visible within the same package.
This would work if the extended classes were in the same package.
I'm having the same problem and I think that lombok does not support this, for now.
I was able to find the feature request in here https://github.com/rzwitserloot/lombok/issues/1489
My suggestion is to hard implement builder pattern in this class.
as mentioned you can use the builder, now instead of user property builder() will return the instance create so you can treat as normal builder ( no need to use property)
instance = MyClass.MyClassBuilder().property1(value1).property1(value2).build();

Java BuilderTestPattern - how to avoid boilerplate?

I have a lot of value objects in my project.
I'm using project lombok to eliminate some boilerplate, so my value objects look like the following one:
#Value
#Accessors(fluent = true)
public class ValueObject {
private final String firstProp;
private final int secondProp;
}
Not bad, almost no boilerplate.
And now, I'm using the all-args constructor quite often in my tests. It looks quite messy, so I thought I will introduce Builder Pattern variant instead:
public class ValueObjectBuilder {
private static final int DEFAULT_VALUE_FOR_SECOND_PROP = 666;
private String firstProp = "default value for first prop;
private int secondProp = DEFAULT_VALUE_FOR_SECOND_PROP;
private ValueObjectBuilder() {}
public static ValueObjectBuilder newValueObject() {
return new ValueObjectBuilder();
}
public ValueObjectBuilder withFirstProp(String firstProp) {
this.firstProp = firstProp
return this;
}
public ValueObjectBuilder withFirstProp(int secondProp) {
this.secondProp = secondProp;
return this;
}
public ValueObject build() {
return new ValueObject(
firstProp, secondProp
);
}
}
and the code looks quite nice now:
ValueObjectBuilder
.newValueObject()
.withFirstProp("prop")
.withSecondProp(15)
.build();
Now, the problem is - as I mentioned, I have to write a lot of similar classes... I'm already tired with copy-paste'ing them.
What I'm looking for, is a black-magic-smart-tool, which will somehow generate this code for me.
I know, there is a #Builder annotation in Lombok, but it doesn't meet my requirements. Here's why:
1) I'm unable to provide default values in lombok's Builder. Well, actually, it is possible - by implementing builder class template myself like
#Builder
public class Foo {
private String prop;
public static class FooBuilder() {
private String prop = "def value";
...
}
}
which generates some boilerplate too.
2) I can't find any way to put prefix on each field accessor in lombok's builder. Maybe #Wither could help here? But I don't know, how to use it properly.
3) The most important reason: I'm not creating a "natural" builder. As far as I understand, lombok is designed to create Builder for a given, annotated class - I don't know if there is a way to return any other object from within build() method.
So, to sum up:
Do you know any tool which could possibly help me? Or maybe all those things I mentioned are in fact possible to achieve using Lombok?
EDIT
Ok, so I probably found a solution to this particular case. With lombok we can use:
#Setter
#Accessors(chain = true, fluent = true)
#NoArgsConstructor(staticName = "newValueObject")
public class ValueObjectBuilder {
private String firstProp = "default";
private int secondProp = 666;
public ValueObject build() {
return new ValueObject(firstProp, secondProp);
}
}
Cheers,
Slawek
I know this is old but if anyone else runs into this, I found an alternative solution for providing default values for a builder.
Override the builder method and provide the default values before returning a builder. So in the above case:
#Builder
public class Foo {
private String prop;
public static FooBuilder builder() {
return new FooBuilder().prop("def value");
}
}
It's not an ideal solution but beats having to override the whole builder itself or have a custom constructor (which is painful IMHO if there are a lot of variables. It would still be nice to have something along the lines of a #With or #Default annotation to handle this.
Try Bob-the-builder for eclipse. Hmm.. I guess that works best if you happen to be using eclipse! If you are not using eclipse, there are a few related projects mentioned at the bottom of the page linked here that may be useful.

Can I use jackson mixIns to modify "getters" behavior?

I'm in the need of do some clean up of some invisible characters (\r\n) and html tags for specific getters on my entities.
I've been trying to use mixIns to modify what's returned from the entity but I'm not sure how can I reference the target class in my MixIn so I can add the clean up logic there. From the my tests seems that not even my method is called.
This is what I have so far, but it never gets called
public abstract class BookMixIn {
#JsonProperty
public String getTitle() {
return StringUtils.deleteWhitespace(getTitle());
}
}
public class Book {
private String title;
// getter/setters omitted...
}
And the ObjectMapper config:
mapper.getSerializationConfig().addMixInAnnotations(com.company.Book.class,
com.company.BookMixIn.class);
mapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
String tmp = mapper.writeValueAsString(book);
log.info(tmp);
Can this be accomplished via MixIns?
Thanks
Jackson mix-ins are purely for associating annotations; they are not used for adding behavior (code).
So they would not help you here.
But the simple way that would work (possibly using mix-in too) is to add annotation for using custom serializer, which can use whatever filtering is needed:
#JsonSerialize(using=MyCoolSerializer.class) public String getTitle() { }
so either add that to POJO, if possible; but if not, associate it using mix-in.
If you are running Jackson 1.9, this works:
BookCleaner cleanBook = new BookCleaner(book);
mapper.getSerializationConfig().addMixInAnnotations(Book.class, BookMixIn.class);
mapper.writeValueAsString(cleanBook);
#JsonSerialize
class BookCleaner {
private Book book;
public BookCleaner(final Book book) { this.book = book; }
#JsonUnwrapped
public Book getBook() { return book; }
#JsonProperty("title")
public String getCleanTitle() { return cleanup(getBook().getTitle()); }
}
public interface BookMixIn {
#JsonIgnore public String getTitle();
}
I don't think it works like this; the class or interface is just used as a signature.
You could use AspectJ to modify the return value, but it might be easier to just create a decorator and serialize that instead of the underlying object.
Alternatively, you could create specific getters for the "safe" versions of things and use the #JsonProperty annotation to give it the name you need, and use #JsonIgnore on the "non-safe" getters.

Categories