Why is Lombok #Builder not compatible with this constructor? - java

I have this simple code:
#Data
#Builder
public class RegistrationInfo {
private String mail;
private String password;
public RegistrationInfo(RegistrationInfo registrationInfo) {
this.mail = registrationInfo.mail;
this.password = registrationInfo.password;
}
}
First I was using only the #Builder Lombok annotation and everything was fine. But I added the constructor and the code does not compile any more. The error is:
Error:(2, 1) java: constructor RegistrationInfo in class com.user.RegistrationInfo cannot be applied to given types;
required: com.user.RegistrationInfo
found: java.lang.String,java.lang.String
reason: actual and formal argument lists differ in length
So I have two questions:
Why is Lombok #Builder not compatible with this constructor?
How do I make the code compile taking into account that I need both the builder and the constructor?

You can either add an #AllArgsConstructor annotation, because
#Builder generates an all-args constructor iff there are no other
constructors defined.
(Quotting #Andrew Tobilko)
Or set an attribute to #Builder : #Builder(toBuilder = true) This give you the functionality of a copy constructor.
#Builder(toBuilder = true)
class Foo {
// fields, etc
}
Foo foo = getReferenceToFooInstance();
Foo copy = foo.toBuilder().build();

When you provide your own constructor then Lombok doesn't create a constructor with all args that #Builder is using. So you should just add annotation #AllArgsConstructor to your class:
#Data //try to avoid as it's an anti-pattern
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class RegistrationInfo {
//...
}

Presumably, #Builder generates an all-args constructor if there are no other constructors defined.
#Data
#Builder
#AllArgsConstructor(access = AccessLevel.PRIVATE)
class RegistrationInfo {
private String mail;
private String password;
private RegistrationInfo(RegistrationInfo registrationInfo) {
this(registrationInfo.mail, registrationInfo.password);
}
}

Related

Deep copy with lombok toBuilder within a nested class - cleaner way?

#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder(toBuilder = true)
public class Example {
public ExampleTwo exampleTwo;
#lombok.Data
#AllArgsConstructor
#NoArgsConstructor
#Builder(toBuilder = true)
public static class ExampleTwo {
private SomeData someData;
private AnotherField anotherField;
}
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder(toBuilder = true)
public class SomeData {
private String specialId;
private String firstName;
private String lastName;
}
So I retrieved an Example instance and made a deep copy to duplicate it. But I want to be able to set one of the fields of the copied object which is specialId from the SomeData nested class. My current working implementation is this:
SomeData someData = example.getExampleTwo().getSomeData().toBuilder().specialID("SPE_1").build();
Example.ExampleTwo exampleTwo = example.getExampleTwo().toBuilder().someData(someData).build();
Example duplicateExample = example.toBuilder().exampleTwo(exampleTwo).build();
Do you have thoughts on ways to make this cleaner without having to go through these additional steps? Would prefer it to be easier to read. I'm avoiding Serializable implementation and declaring it as a Cloneable interface since I've read to avoid those. Last resort would be to use a library.
#With might be helpful:
Example example = Example.builder().build();
Example.ExampleTwo exampleTwo = example.getExampleTwo();
SomeData someData = exampleTwo.getSomeData();
return example.withExampleTwo(
exampleTwo.withSomeData(
someData.withSpecialId("SPE_1")
)
);

Cannot map nested field with #Mapper

I'm trying to create a mapper using lombok #Mapper and I get the following error:
Property "delivery" has no write accessor in ClassA for target name "classBB_variable.delivery"
The code:
#Mapper
#Mapping(target = "classBB_variable.customerId", source = "classA_variable.customerId")
#Mapping(target = "classBB_variable.delivery", source = "classA_variable.classAA_variable.delivery")
ClassB toCreateRequest(ClassA classA_variable);
#Value
#Builder(toBuilder = true)
public class ClassA {
private UUID customerId;
private ClassAA classAA_variable;
}
#Data
#Builder
public class ClassAA {
private String delivery;
}
#Data
#Builder
public class ClassB {
private ClassBB classBB_variable;
}
#Data
#Builder
public class ClassBB {
private UUID customerId;
private String delivery;
}
As the map for customerId works fine, I'm assuming the issue is the additional level introduced with classA_variable.classAA_variable.delivery.
Has anyone already dealt with this situation? How can I solve it?

Lombok #SuperBuilder doesn't take parameters

Assuming two simple classes:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class Party {
protected Long id;
protected String status;
}
#Data
#SuperBuilder
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(callSuper = true)
public class Person extends Party {
private String name;
private Long sex;
}
The compilation fails on the following error. Upon reading Lombok | #SuperBuilder I have no idea what I could miss.
C:\Dev\companyproject\src\main\java\com\companyproject\entity\Person.java:12
java: type com.companyproject.entity.Party.PartyBuilder does not take parameters
The issue here is the incorrect #Builder annotation on the parent class. The documentation for #SuperBuilder mentions:
Most importantly, it requires that all superclasses also have the #SuperBuilder annotation.
So the correct parent class would be:
#Data
#SuperBuilder // <- annotation replaced here
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class Party {
protected Long id;
protected String status;
}
Addendum:
A key difference between both annotations is that #SuperBuilder also creates a constructor of the class expecting a corresponding builder argument.
For Party it would look like:
protected Party(PartyBuilder<?, ?> b) {
this.id = b.id;
this.status = b.status;
}
and for Person:
protected Person(PersonBuilder<?, ?> b) {
super(b);
this.name = b.name;
this.sex = b.sex;
}
As you can see, the child class constructor wants to pass its own builder to the parent class constructor and this will only be possible if there is a matching constructor there, and #Builder wouldn't generate it.
Also PersonBuilder<> extends PartyBuilder<>, that is why calling super with the child type builder works fine here.

Builders on abstract classes cannot be #Jacksonized (the builder would never be used)

I have two classes OAuth2Token and CachedOAuth2Token that extends a class called AbstractOAuth2Token.
AbstractOAuth2Token.java
#SuperBuilder
#Jacksonized
#JsonSubTypes({
#JsonSubTypes.Type(value = OAuth2Token.class),
})
#Getter
#Setter
#ToString
public abstract class AbstractOAuth2Token {
#JsonProperty("access_token")
private String accessToken;
#JsonProperty("token_type")
private String tokenType;
}
OAuth2Token.java
#Getter
#Setter
#SuperBuilder
#ToString(callSuper = true)
#JsonTypeName("OAuth2Token")
#Jacksonized
public class OAuth2Token extends AbstractOAuth2Token {
#JsonProperty("expires_in")
private int expiresIn;
}
CachedOAuth2Token.java
#Getter
#Setter
#SuperBuilder
#ToString(callSuper = true)
public class CachedOAuth2Token extends AbstractOAuth2Token {
private LocalDateTime expirationDate;
}
Unfortunately my Maven project doesn't build because AbstractOAuth2Token.java: Builders on abstract classes cannot be #Jacksonized (the builder would never be used).
Even if the code works as expected if the AbstractOAuth2Token isn't abstract, then I'm able to create an instance of it using the builder which indeed isn't what I want. Its constructor is protected, so no problem there.
The idea is that I want AbstractOAuth2Token to be abstract without losing any fuctionality in the children. I'm a fan of Lombok, so I want to be able to use the autogenerated builders but together with Jackson.
It's a Wildfly 11 project with Lombok 1.18.16
How can I solve this issue?
Don't add #Jacksonized to your abstract base class. Non-#Jacksonized #SuperBuilders are compatible with #Jacksonized #SuperBuilders. As Jackson will never use AbstractOAuth2Token's builder directly, there is no need to configure it for Jackson explicitly.

JsonDeserialize set inherited properties also when objectmapper readvalue

JsonDeserialize not working when objectmapper readvalue for inherited properties.
Vehicle Class
#Getter
#Setter
#JsonDeserialize(builder = Vehicle.VehicleBuilder.class)
#Builder(builderClassName = "VehicleBuilder", toBuilder = true)
public class Vehicle{
private String name;
private String noOfTyres;
#JsonPOJOBuilder(withPrefix = "")
public static class VehicleBuilder{
}
}
Car class
#Getter
#Setter
#JsonDeserialize(builder = Car.CarBuilder.class)
#Builder(builderClassName = "CarBuilder", toBuilder = true)
public class Car extends Vehicle {
private String carType;
#JsonPOJOBuilder(withPrefix = "")
public static class CarBuilder extends VehicleBuilder {
}
}
I don't want to create #NoArgsConstructor ,#AllArgsConstructor in both classes.
My issue when Car car = om.readValue(jsonValue,Car.class);
When I parse Json to java object the parent class properties are not setting properly.
As of now I'm using #NoArgsConstructor ,#AllArgsConstructor for work around for the use case.
Is there any way to use it along with #JsonDeserialize and #JsonPOJOBuilder?
The problem with the code is that it assumes that builders in inherited classes will set the parent properties as well. Unfortunately, they don't do that out of the box. However, this is something that can be achieved with Lombok but requires some additional code, as described in this post.
A complete solution could look as follows.
Parent Class
#Getter
#Setter
#JsonDeserialize
#Builder(builderClassName = "VehicleBuilder", builderMethodName = "vehicleBuilder")
public class Vehicle {
private String name;
private String noOfTyres;
}
Child Class
#Getter
#Setter
#JsonDeserialize(builder = Car.CarBuilder.class)
public class Car extends Vehicle {
private String carType;
#Builder
public Car(String name, String noOfTyres, String carType) {
super(name, noOfTyres);
this.carType = carType;
}
#JsonPOJOBuilder(withPrefix = "")
public static class CarBuilder extends VehicleBuilder {
}
}
Notice that the builder on the extending class is achieved by supplying a constructor with the #Builder annotation. Also take notice that the extending class does not set annotation parameter toBuilder=true as that will require access to parent properties which are private. This can be achieved by setting parent class properties to protected.

Categories

Resources