Error while use builder(lombok) in constructor annotation - java

#Data
#Builder
public static class Common {
private String common1;
private String common2;
}
#Getter
public static class Special extends Common {
private String special1;
#Builder
public Special(String common1, String common2, String special1) {
super(common1, common2);
this.special1 = special1;
}
}
The below error occurs :
Error:(149, 9) java: builder() in com.example.home.ExampleDTO.Special cannot override builder() in com.example.home.ExampleDTO.Common
return type com.example.home.ExampleDTO.Special.SpecialBuilder is not compatible with com.example.home.ExampleDTO.Common.CommonBuilder
And when I put (builderMethodName = "b") this parameter in #Builder(Special constructor) then it works fine.
#Builder(builderMethodName = "b")
public Special(String common1, String common2, String special1) {
I have no idea, why the first code gives error.
Please help me out.
Thank you

#Builder creates a static method builder() in both classes; it returns an instance of the respective builder. But the return types of the methods are not compatible, because SpecialBuilder and CommonBuilder are different and unrelated classes: #Builder does not (and can not technically) consider the inheritance relation between the classes. So the compiler complains about two methods with the same name, no arguments, but different return types. This is not possible in Java.
To solve this you have two choices:
Use #SuperBuilder on both classes. #SuperBuilder is designed to work with inheritance.
As you already found out, you can rename the method in one of the classes.

Related

How to override #DefaultValue in child classes

In our project we haven't Spring, so work like can with that. I found that we have many repetable code.
We have something like that in 5 or more classes
import javax.ws.rs.DefaultValue;
import javax.ws.rs.QueryParam;
#Data
public class QueryParamDto {
#DefaultValue("1");
#QueryParam("sortBy")
private String sortBy;
#DefaultValue("ASC");
#QueryParam("sortDir")
private String sortDir;
...
}
And it would be good to create something like BaseDto class with common fields, but in some classes we have different DefaultValue like
#Data
public class KeywordsDto {
#DefaultValue("5");
#QueryParam("sortBy")
private String sortBy;
...
}
Because of that 'sortBy' can not be coommon fields with common value = 1 from BaseDto.
Maybe there is some variant to oveeride child's fields?
you can just modify it in your child class without the need of overriding, declare it in the father class and give it the value in the child. or check out this answer, maybe could help Overriding member variables in Java ( Variable Hiding)

How to return a List inside this bounded generic method?

I have class Response
public class Response<T extends ResponseData> {
private final T data;
//other fields, getters, setters ...
}
and the empty interface ResponseData:
public interface ResponseData {
}
It takes any object as data but this object must implement the "empty" interface which I created just to force all classes returned inside "data" element be of same super type
Now the problem is that I need to return a List inside the data element, but:
I don't find it clean to create a ResponseData implementation which serves only as a wrapper around my List
I can't return the List directly because it doesn't belong to my code and therefore I can't make it implement the marker interface (ResponseData)
So is there a solution to my problem other than the one that says I should delete the marker interface and allow any object to be returned in the data element?
n.b. : My purpose of the marker interface is to make any created classes which will be returned inside the data element inside the response,for anyone who reads them, clear towards their purpose of existence
n.b. : As mentioned in number 1 above, I know that this solution exists:
class ResponseDataWrapper implements ResponseData{
private final List<SomeType> responseList;
//getters,setters
}
but it is not clean as in this case there is a layer of nesting (i.e. the wrapper class) which is not necessary between the List and the "data" element in the response
Have a response object like this that Returns the list:
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Data
public class SomeCoolResponseForAnApi implements ResponseData{
private List<SomeObject> theListYouWantToReturn;
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Data
public static class SomeObject{
private String name;
private String age;
}
}
PS: I have used lombok, you can just use regular getters and setters.

Use custom setter in Lombok's builder with superclass

I want to use custom setter in Lombok's builder and overwrite 1 method, like this
#SuperBuilder
public class User implements Employee {
private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();
private String username;
private String password;
public static class UserBuilder {
public UserBuilder password(String password) {
this.password = ENCODER.encode(password);
return this;
}
}
}
but I have this compilation error
Existing Builder must be an abstract static inner class.
In contrast to #Builder, #SuperBuilder generates two builder classes, a public and a private one. Both are heavily loaded with generics to ensure correct type inference.
If you want to add or modify a method to the builder class, you should have a look at the uncustomized delomboked code and copy&paste the public abstract static class header from there. Otherwise you'll likely get the generics wrong, leading to compiler errors you won't be able to fix. Also have a look at the return types and statements of the generated methods to make sure you define that correctly.
The #SuperBuilder documentation also mentions this:
Due to the heavy generics usage, we strongly advice to copy the builder class definition header from the uncustomized delomboked code.
In your case, you have to customize the builder as follows:
public static abstract class UserBuilder<C extends User, B extends User.UserBuilder<C, B>> {
public B password(final int password) {
this.password = ENCODER.encode(password);
return self();
}
}

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();

Categories