Given I have the POJO:
import lombok.Builder;
import lombok.Data;
#Data
#Builder
public class SomeResponse {
private String author;
private String authorTitle;
private String teaser;
private String text;
private Long lastModified;
private Long created;
private Integer rating;
private Optional<Markdown> markdown;
private Optional<Integer> wordCount;
}
When I try to use the POJO in such normal Jackson construction:
restTemplate.getForObject(urlTemplate, SomeResponse.class,
productId.toString(), siteId.toString());
I get an exception, because there are private ctor in the SomeResponse class due to Lombok #Builder annotation.
How can I make it works without deleting Lombok #Builder annotation?
Also add #AllArgsConstructor and #NoArgsConstructor, possible with the right access values. See the documentation for appropriate parameters.
Disclosure: I am a lombok developer.
Related
How to model this in Lombok, constructors which take lesser arguments and supporting get/set for transient methods. How to tweak the following definitions
#NoArgsConstructor
#AllArgsConstructor
#Data
public class SalaryRange {
private Integer from;
private Integer to;
private transient String displayName;
private SalaryRange() {
}
private SalaryRange(Integer from) {
this(from, null);
}
private SalaryRange(Integer from, Integer to) {
this(from, to, null);
}
private SalaryRange(Integer from, Integer to, String displayName) {
this.from = from;
this.to = to;
this.displayName = displayName;
}
..
}
Just use a Builder and NoArgsConstructor
#NoArgsConstructor
#Builder
#Data
public class SalaryRange {
private Integer from;
private Integer to;
private transient String displayName;
}
and then
SalaryRange range = SalaryRange.builder().from(1).to(2).build();
Documentation:
Project Lombok Builder
One additional notice - when using #Builder do not static import Builder class - there is a bug that, as far as I know, is not fixed yet
static import not working in lombok builder in intelliJ
I don't think Lombok provide that much flexibility with constructor annotation. 2 approaches may help:
Use #RequiredArgsConstructor, which takes in all final variables.
#NoArgsConstructor
#AllArgsConstructor
#Data
public class SalaryRange {
private final Integer from;
private final Integer to;
private transient String displayName;
}
Use #Builder to achieve more flexibility. However, you may still need #NoArgsConstructor #AllArgsConstructor sometimes for serialization/deserialization by thirty party tools integration, e.g. spring rest, mybatis, etc. Just try it out and check logs.
Otherwise, just write constructor manually, intellij can generate constructor for you quickly by command + N
You may keep constructors from below, the other two are generated by #NoArgsConstructor and #AllArgsConstructor, constructors are meant to build objects
private SalaryRange(Integer from) {
this(from, null);
}
private SalaryRange(Integer from, Integer to) {
this(from, to, null);
}
Another approach might be to combine #Accessors (experimental feature) with #NoArgsConstructor and #AllArgsConstructor
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Accessors(chain = true)
public class SalaryRange {
private Integer from;
private Integer to;
private transient String displayName;
}
SalaryRange salaryRange1 = new SalaryRange().setFrom(1).setTo(2);
SalaryRange salaryRange2 = new SalaryRange(1, 2, "somename");
You may use #Accessors(chain = true, fluent = true) to have fluent accessors if you don't require the get & set prefix
SalaryRange salaryRange1 = new SalaryRange().from(1).to(2);
PD: Builder pattern has downsides that might lead to an Anti-Pattern and might hide a bad design, so be careful when you decide to use it. I think Builder pattern is overused in this scenario, we don't need to add extra complexity to build an object with three attributes.
https://www.baeldung.com/lombok-accessors
https://www.yegor256.com/2016/02/03/design-patterns-and-anti-patterns.html
I have this simple class
public class ErrorDetails {
private String param = null;
private String moreInfo = null;
private String reason = null;
...
}
After refactoring, I added #Data and #Builder, but all the instantiations doesn't work any more
ErrorDetails errorDetails = new ErrorDetails();
'ErrorDetails(java.lang.String, java.lang.String, java.lang.String)'
is not public in
'com.nordea.openbanking.payments.common.ndf.client.model.error.ErrorDetails'.
Cannot be accessed from outside package
If I removed #Builder, then it will work fine,
Why I cannot use #Data and #Builder together?
Lombok's #Builder must have #AllArgsConstructor in order to work
Adding also #AllArgsConstructor should do
Under the hood it build all fields using constructor with all fields
applying #Builder to a class is as if you added #AllArgsConstructor(access = AccessLevel.PACKAGE) to the class and applied the #Builder annotation to this all-args-constructor. This only works if you haven't written any explicit constructors yourself.
The full config should be :
#Data
#Builder(toBuilder = true)
#AllArgsConstructor
#NoArgsConstructor
class ErrorDetails {
private String param; // no need to initiate with null
private String moreInfo;
private String reason;
}
Here is the class
package com.bablo.domain;
import com.google.gson.annotation.SerializedName;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
#Getter #Setter
#ToString
public class PC {
#SerializeName("name")
private String name;
#SerializeName("model")
private String model;
#SerializeName("processor")
private String processor;
}
In the above class attribute for model can have an empty string in the json, how to give it default value at the time of serialization and deserialization.
That is in the requestbody json the value for the model key can have empty string as a value, how to read it and put instead default values in the place of empty string in the class attribute.
Should I disable lombok and add custom getter and setter methods in my class to override the default values?
Or is there any annotation available in Gson to achieve this?
Or lombok has some workaround to achieve this?
Probably, you need to use lombok Builder annotation here. Something like this:
#Getter #Setter
#ToString
class PC {
#SerializedName("name")
#Builder.Default
private String name = "name1";
#SerializedName("model")
#Builder.Default
private String model = "model1";
#SerializedName("processor")
#Builder.Default
private String processor = "processor1";
}
I was setting the value of recordId from the child classes using the default constructor and was not using lombok #Builder initially. Eventually i decided to use the Builder here, but the problem now is lombok Builder overrides my default constructor internally hence the value is never set.
How can I put any hook too make lombok #Builder use my default constructor?
Parent class:
#Getter
#Setter
public abstract class Record {
private String recordId;
}
Child class:
#Getter
#Setter
#Builder
#ToString
#AllArgsConstructor
public class SRecord extends Record {
private static final String RECORD_ID = "REC001";
private String street;
private String city;
public SRecord() {
setRecordId(RECORD_ID); //value of recordId being set
}
}
Lombok's #Builder simply does not use the default constructor. It passes its values to an all-args constructor so that this constructor can fill the new instance with these values. #Builder does not use setters or direct access to the fields to do so. So your default constructor is simply ignored by #Builder.
What you can do is write your own all-args constructor. In it, you set your value for recordId and assign the rest of the fields from the parameters.
I think you should create a constructor in your base class:
#Getter
#Setter
public abstract class Record {
private String recordId;
public Record(String recordId) {
this.recordId = recordId;
}
}
Then use it in the constructor of the inherited class:
#Getter
#Setter
#Builder
public class SRecord extends Record {
private static final String RECORD_ID = "REC001";
private String street;
private String city;
public SRecord(String street, String city) {
super(RECORD_ID);
this.street = street;
this.city = city;
}
}
P.S. If you want to use Lombok Builder with inheritance you can use this technique.
I have a simple java pojo which i give to my android users:
#XmlRootElement
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class PostAccount {
#Getter
#Setter
private String email;
#Getter
#Setter
private String pass1;
#Getter
#Setter
private String pass2;
}
This pojo is serialized to json and send to my server. On my server i which to use jersey bean validation:
public NumberResult add(#Valid ValidAccount account) {
But because the Account pojo doesn't have any validation annotations validation doesn't do much.
I can create a second pojo with validation annotations and use that on server side:
public class ValidAccount {
#Getter
#Setter
#NotEmpty
#CheckEmail
private String email;
#Getter
#Setter
#NotBlank
private String pass1;
#Getter
#Setter
#NotBlank
private String pass2;
}
Works perfectly.
But when i now add a field at Account pojo i do have to remember to change ValidAccount pojo. No problem, but when you have a lot of pojo's things get complicated to manage.
Is there a better solution?
For example is it possible to extends the Account pojo and in a way add the validation rules? (please i which to continue using annotations, xml gives me the creeps)