Overview
@Data is a convenient shortcut annotation that bundles the features of @ToString,
@EqualsAndHashCode, @Getter / @Setter
and @RequiredArgsConstructor together: In other words, @Data generates all the
boilerplate that is normally associated with simple POJOs (Plain Old Java Objects) and beans: getters for all fields, setters for all non-final fields,
and appropriate toString, equals and hashCode implementations that involve the fields of the class, and a constructor that
initializes all final fields, as well as all non-final fields with no initializer that have been marked with @NonNull,
in order to ensure the field is never null.
@Data is like having implicit @Getter, @Setter, @ToString, @EqualsAndHashCode and @RequiredArgsConstructor
annotations on the class. However, the parameters of these annotations (such as callSuper, includeFieldNames and
exclude) cannot be set with @Data. If you need to set non-default values for any of these parameters, just add those annotations
explicitly; @Data is smart enough to defer to those annotations.
All generated getters and setters will be public. To override the access level, annotate the field or class with an explicit @Setter and/or
@Getter annotation. You can also use this annotation (by combining it with AccessLevel.NONE) to suppress generating a getter and/or setter
altogether.
All fields marked as transient will not be considered for hashCode and equals. All static fields will be
skipped entirely (not considered for any of the generated methods, and no setter/getter will be made for them).
If the class already contains a method with the same name as any method that would normally be generated, that method is not generated, and no warning or
error is emitted. For example, if you already have a method with signature void hashCode(int a, int b, int c), no int hashCode()
method will be generated, even though technically int hashCode() is an entirely different method. The same rule applies to the constructor,
toString, equals, and all getters and setters.
@Data can handle generics parameters for fields just fine. In order to reduce the boilerplate when constructing objects for classes with
generics, you can use the staticConstructor parameter to generate a private constructor, as well as a static method that returns a new
instance. This way, javac will infer the variable name. Thus, by declaring like so: @Data(staticConstructor="of") class Foo<T> { private T x;}
you can create new instances of Foo by writing: Foo.of(5); instead of having to write: new Foo<Integer>(5);.
With Lombok
01 import lombok.AccessLevel;
02 import lombok.Setter;
03 import lombok.Data;
04 import lombok.ToString;
05
06 @Data public class DataExample {
07 private final String name;
08 @Setter(AccessLevel.PACKAGE) private int age;
09 private double score;
10 private String[] tags;
11
12 @ToString(includeFieldNames=true)
13 @Data(staticConstructor="of")
14 public static class Exercise<T> {
15 private final String name;
16 private final T value;
17 }
18 }
Vanilla Java
001 import java.util.Arrays;
002
003 public class DataExample {
004 private final String name;
005 private int age;
006 private double score;
007 private String[] tags;
008
009 public DataExample(String name) {
010 this.name = name;
011 }
012
013 public String getName() {
014 return this.name;
015 }
016
017 void setAge(int age) {
018 this.age = age;
019 }
020
021 public int getAge() {
022 return this.age;
023 }
024
025 public void setScore(double score) {
026 this.score = score;
027 }
028
029 public double getScore() {
030 return this.score;
031 }
032
033 public String[] getTags() {
034 return this.tags;
035 }
036
037 public void setTags(String[] tags) {
038 this.tags = tags;
039 }
040
041 @Override public String toString() {
042 return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
043 }
044
045 @Override public boolean equals(Object o) {
046 if (o == this) return true;
047 if (!(o instanceof DataExample)) return false;
048 DataExample other = (DataExample) o;
049 if (!other.canEqual((Object)this)) return false;
050 if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
051 if (this.getAge() != other.getAge()) return false;
052 if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
053 if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
054 return true;
055 }
056
057 @Override public int hashCode() {
058 final int PRIME = 31;
059 int result = 1;
060 final long temp1 = Double.doubleToLongBits(this.getScore());
061 result = (result*PRIME) + (this.getName() == null ? 0 : this.getName().hashCode());
062 result = (result*PRIME) + this.getAge();
063 result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
064 result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
065 return result;
066 }
067
068 public static class Exercise<T> {
069 private final String name;
070 private final T value;
071
072 private Exercise(String name, T value) {
073 this.name = name;
074 this.value = value;
075 }
076
077 public static <T> Exercise<T> of(String name, T value) {
078 return new Exercise<T>(name, value);
079 }
080
081 public String getName() {
082 return this.name;
083 }
084
085 public T getValue() {
086 return this.value;
087 }
088
089 @Override public String toString() {
090 return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
091 }
092
093 @Override public boolean equals(Object o) {
094 if (o == this) return true;
095 if (!(o instanceof Exercise)) return false;
096 Exercise<?> other = (Exercise<?>) o;
097 if (!other.canEqual((Object)this)) return false;
098 if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
099 if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
100 return true;
101 }
102
103 @Override public int hashCode() {
104 final int PRIME = 31;
105 int result = 1;
106 result = (result*PRIME) + (this.getName() == null ? 0 : this.getName().hashCode());
107 result = (result*PRIME) + (this.getValue() == null ? 0 : this.getValue().hashCode());
108 return result;
109 }
110 }
111 }
Small print
See the small print of @ToString, @EqualsAndHashCode,
@Getter / @Setter and @RequiredArgsConstructor.
Any annotations named @NonNull (case insensitive) on a field are interpreted as: This field must not ever hold
null. Therefore, these annotations result in an explicit null check in the generated constructor for the provided field. Also, these
annotations (as well as any annotation named @Nullable) are copied to the constructor parameter, in both the true constructor and
any static constructor. The same principle applies to generated getters and setters (see the documentation for @Getter / @Setter)
By default, any variables that start with a $ symbol are excluded automatically.
You can include them by specifying an explicit annotation (@Getter or @ToString, for example) and using the 'of' parameter.