If I specify #AllArgsConstructor using Lombok, it will generate a constructor for setting all the declared (not final, not static) fields.
Is it possible to omit some field and this leave generated constructor for all other fields?
No that is not possible. There is a feature request to create a #SomeArgsConstructor where you can specify a list of involved fields.
Full disclosure: I am one of the core Project Lombok developers.
Alternatively, you could use #RequiredArgsConstructor. This adds a constructor for all fields that are either #NonNull or final.
See the documentation
Just in case it helps, initialized final fields are excluded.
#AllArgsConstructor
class SomeClass {
final String s;
final int i;
final List<String> list = new ArrayList<>(); // excluded in constructor
}
var x = new SomeClass("hello", 1);
It makes sense especially for collections, or other mutable classes.
This solution can be used together with the other solution here, about using #RequiredArgsConstructor:
#RequiredArgsConstructor
class SomeClass2 {
final String s;
int i; // excluded because it's not final
final List<String> list = new ArrayList<>(); // excluded because it's initialized
}
var x = new SomeClass2("hello");
A good way to go around it in some cases would be to use the #Builder
This can be done using two annotations from lombok #RequiredArgsConstructor and #NonNull.
Please find the example as follows
package com.ss.model;
import lombok.*;
#Getter
#Setter
#RequiredArgsConstructor
#ToString
public class Employee {
private int id;
#NonNull
private String firstName;
#NonNull
private String lastName;
#NonNull
private int age;
#NonNull
private String address;
}
And then you can create an object as below
Employee employee = new Employee("FirstName", "LastName", 27, "Address");
Lombok is meant to take care of the boilerplate code for your POJOs. Customized constructors/setters/getters/toString/copy etc are not on the boilerplate side of code. For these cases, every Java IDE provide easy to use code generators to help you do things in no time.
In your case a
public MyClass(String firstName, String lastName) {....}
is much more readable and makes more sense than a hypothetic:
#AllArgsConstructor(exclude = "id", exclude = "phone")
Have fun!
Related
I'm running into an issue with my pojo created using lombok with jsonproperty annotation. It doesn't respect the json annotation. And, when i create an object using the lombok builder it uses the field names on the object instead of json property.
Could someone help see what am I missing here. I just started using lombok so im hoping something straightforward. I'm running the code on Intellij
#Data
#Builder
public class pojo {
#JsonProperty("grant_type")
private final String grantType = "xyz";
#JsonProperty("client_id")
private String clientId;
}
It's default behavior of #Builder.
If we want the builder with setClientId, We can add setterPrefix = "set" into #Builder.
#Data
#Builder(setterPrefix = "set")
public class pojo {
#JsonProperty("grant_type")
private final String grantType = "xyz";
#JsonProperty("client_id")
private String clientId;
}
#Data would generate a pair of setter/getter. But setter is a member method of pojo, not pojoBuilder's.
More details of Builder is here.
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 am getting below error while using lombok and even it doesn't allow me to set id and version while creating student instance.
Multiple markers at this line
- overrides com.example.demo.IModel.canEqual
- Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is
intentional, add '#EqualsAndHashCode(callSuper=false)' to your type.
- overrides com.example.demo.IModel.hashCode
- overrides com.example.demo.IModel.toString
- overrides com.example.demo.IModel.equals
IModel
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class IModel {
private String id;
private String version;
}
Student
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class Student extends IModel{
private String firstName;
private String lastName;
}
In the main method, it doesn't allow me to set the value of Id and version field
Student s = Student.builder().firstName("Adam").lastName("Kerr").build();
Edit-1
#sfiss - As suggested, now I changed like below, but now I am not able to set firstName and lastName, only cab set id and version
Student.java
#Data
#Builder(builderMethodName = "studentBuilder")
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(callSuper = true)
public class Student extends IModel {
#NotEmpty(message = "{email.notempty}")
#Email
private String firstName;
private String lastName;
public Student(final String firstName, final String lastName, final String id, final String version) {
super(id, version);
this.firstName = firstName;
this.lastName = lastName;
}
}
IModel.java
#Builder
#Data
#NoArgsConstructor
#AllArgsConstructor
public class IModel {
private String id;
private String version;
}
There are multiple problems here, all of them relating to using lombok with inheritance:
Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is
intentional, add '#EqualsAndHashCode(callSuper=false)' to your type.
The warning is given by #Data because that usually generates equals/hashcode without the call to super. Fix it by adding #EqualsAndHashCode(callSuper = true).
The #Builder gives you a compile warning because it will generate two static methods with the same name in both the super- and the subclass. Fix it by defining #Builder(builderMethodName = "studentBuilder") on Student.
You won't be able to set superclass properties on you studentBuilder because your superclass and subclass have a default constructor. Fix it by creating a constructor and moving the #Builder annotation to it (i.e. annotate the constructor with #Builder, not the class):
Code:
#Builder(builderMethodName = "studentBuilder")
public Student(
final String firstName,
final String lastName,
final String id,
final String version) {
super(id, version);
this.firstName = firstName;
this.lastName = lastName;
}
Call your builder with the correct method (IModel.builder() vs Student.studentBuilder()):
Student.studentBuilder().firstName("Name").build();
I also want to add some improvements to the above solution. While I like lombok as a tool (I really don't need to read that much boilerplate), the first solution to preventing boilerplate is to think whether you need all those getters and setters and ask yourself these questions:
Do you want bags of data? It is fine for some use cases, in others you want objects more in the sense of OOP, i.e. don't expose your state but behavior.
Do you really need mutability? If not, prefer #Value.
Do you really need both constructor types (especially the no-args-constructor)? They are part of the problem here. Sometimes you need them for frameworks (proxies, reflection, ...) to work properly.
More specific to your code: You prefixed the superclass with "I" but it is not an interface. If it is meant as an abstract class, declare it abstract and don't give it a #Builder.
You can use #sfiss solution
or
You can use #Getter and #Setter annotations instead of #Data annotation.
The warning from building a Spring Boot project
When I built my Spring boot project, I got 20 warnings about a same thing. The warning shows: Generating equals/hashCode implementation but without a call to superclass
Description of the warning
This warning is from lombook, it happens when we inherit a child class from parent class by using #Data #ToString #EqualsAndHashCode, IDE will trigger the warning: Generating equals/hashCode implementation but without a call to superclass .
Solution
There are two solutions:
add annotation #EqualsAndHashCode(callSuper = true) on the class
create lombook config file in the project root path: src/main/java. Note: this solution requires the version of lombook > 1.14.
I recommend the solution 2, since you will not need to add the annotation to all the required classes.
To impletement the solution, you need to create lombok.config in the path of src/main/java. If you have more than one packages, you may need to create multiple config files.
The content of the config file includes:
config.stopBubbling=true
lombok.equalsAndHashCode.callSuper=call
When we rebuild our project, you will not get these warnings anymore.
Cheers!
I had the same problem and i resolved it in this way hope it helps you.
That help you also with abstract classes
Student.java
#Data
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(callSuper = true)
public class Student extends IModel {
#NotEmpty(message = "{email.notempty}")
#Email
private String firstName;
private String lastName;
#Builder
public Student(final String firstName, final String lastName, final String id, final String version) {
super(id, version);
this.firstName = firstName;
this.lastName = lastName;
}
}
IModel.java
#Data
#NoArgsConstructor
#AllArgsConstructor
public class IModel {
private String id;
private String version;
}
The error says If this is
intentional, add '#EqualsAndHashCode(callSuper=false)' to your type.
So, add #EqualsAndHashCode(callSuper = false) to solve the problem.
Using json to save and load data requires a constructor for json to load the object, and I'm having trouble getting lombok annotations to work with this. What should I do?
This is what my class looked like before and after attempting to use an annotation to construct my item:
#Data
public class Item { //before
private int id;
private int amount;
public Item(#JsonProperty("id") int id, #JsonProperty("amount") int amount) {
this.id = id;
this.amount = amount;
}
}
#Data
#AllArgsConstructor
#NoArgsConstructor //I don't want this here as it could cause complications in other places. But json requires I have this...
public class Item { //after
private int id;
private int amount;
}
I don't want to use the NoArgsConstructor annotation by lombok as I don't want a no args constructor for this class. I realise that I could do this:
private Item() {
}
But was hoping there is a better way...
Since lombok 1.18.4, you can configure what annotations are copied to the constructor parameters. Insert this into your lombok.config:
lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty
Then just add #JsonProperty to your fields:
#Data
#AllArgsConstructor
public class Item {
#JsonProperty("id")
private int id;
#JsonProperty("amount")
private int amount;
}
Although the annotation parameters may seem unnecessary, they are in fact required, because at runtime the names of the constructor parameters are not available.
try adding this to your lombok config file:
lombok.anyConstructor.addConstructorProperties=true
config.stopBubbling = true
So what you're saying is that Jackson requires no-args constructor for deserialization, and you don't want to add no-args constructors to your classes because that doesn't play well with your model.
Lombok is completely irrelevant here - it makes zero difference whether no-args constructor would be written manually or generated by Lombok, it'll still be just a no-args constructor.
Your real question is - can I make Jackson work without no-argument constructors on target classes. There are multiple answers to that already, you have almost done it. Here's what has to be done:
Add #JsonCreator annotation to your constructor
Add #JsonProperty("propName") to constructor parameters
You did the #2 but not #1. Add that and this should fix your problem.
Is it possible to store something like the following using only one table? Right now, what hibernate will do is create two tables, one for Families and one for people. I would like for the familymembers object to be serialized into the column in the database.
#Entity(name = "family")
class Family{
private final List<Person> familyMembers;
}
class Person{
String firstName, lastName;
int age;
}
This is an horrible design and I'm really not recommending it (you should just create another table) but it is possible.
First, you'll need to use a byte[] attribute to hold a serialized version of the list of persons that will be stored in a BLOB in the database. So annotate it's getter with #Lob (I would make the getter and setter private to not expose them). Then, expose "fake" getter and setter to return or set a List<Person> from the byte[]. I'm using SerializationUtils from Commons Lang in the sample below (provide you own helper class if you don't want to import this library) to serialize/deserialize on the fly to/from the byte[]. Don't forget to mark the "fake" getter with #Transcient or Hibernate will try to create a field (and fail because it won't be able to determine the type for a List).
#Entity(name = "family")
class Family implements Serializable {
// ...
private byte[] familyMembersAsByteArray;
public Family() {}
#Lob
#Column(name = "members", length = Integer.MAX_VALUE - 1)
private byte[] getFamilyMembersAsByteArray() { // not exposed
return familyMembersAsByteArray;
}
private void setFamilyMembersAsByteArray((byte[] familyMembersAsByteArray() { // not exposed
this.familyMembersAsByteArray = familyMembersAsByteArray;
}
#Transient
public List<Person> getFamilyMembers() {
return (List<Person>) SerializationUtils.deserialize(familyMembersAsByteArray);
}
public void setParticipants(List familyMembers) {
this.familyMembersAsByteArray = SerializationUtils.serialize((Serializable) familyMembers);
}
}
Don't forget to make the Person class Serializable and to add a real serialVersionUID (I'm just showing a default here):
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
// ...
private String firstName, lastName;
private int age;
}
But, let me insist, this is an horrible design and it will be very fragile (changing Person might require to "migrate" the content of the BLOB to avoid deserialization issues and this will become painful. You should really reconsider this idea and use another table for the Person instead (or I don't get why you use a database).
#Type(type = "serializable")
private List<Person> familyMembers;
if you can't use hibernate annotations try this:
#Lob
private Serializable familyMembers;
public List<Person> getFamilyMembers(){
return (List) familyMembers;
}
public void setFamilyMembers(List<Person> family){
familyMembers = family;
}
Annotate the property with #Column and define the type to be ArrayList, not just List. And make Person implement Serializable.
But you should do this only if your motives are very clear, because this is the correct solution in some very rare cases. As Pascal noted, if you ever have to change Person you'll have headaches.
You can create pseudoproperty (getter and setter) which accepts/returns the serialized form, and annotate the familyMembers with #Transient. This would also need to annotate the getters, not fields, for all other properties.