@Value

Since

@Value was introduced as experimental feature in lombok v0.11.4.

@Value no longer implies @Wither since lombok v0.11.8.

@Value promoted to the main lombok package since lombok v0.12.0.

Overview

@Value is the immutable variant of @Data; all fields are made private and final by default, and setters are not generated. The class itself is also made final by default, because immutability is not something that can be forced onto a subclass. Like @Data, useful toString(), equals() and hashCode() methods are also generated, each field gets a getter method, and a constructor that covers every argument (except final fields that are initialized in the field declaration) is also generated.

In practice, @Value is shorthand for: final @ToString @EqualsAndHashCode @AllArgsConstructor @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @Getter, except that explicitly including an implementation of any of the relevant methods simply means that part won't be generated and no warning will be emitted. For example, if you write your own toString, no error occurs, and lombok will not generate a toString. Also, any explicit constructor, no matter the arguments list, implies lombok will not generate a constructor. If you do want lombok to generate the all-args constructor, add @AllArgsConstructor to the class. You can mark any constructor or method with @lombok.experimental.Tolerate to hide them from lombok.

It is possible to override the final-by-default and private-by-default behavior using either an explicit access level on a field, or by using the @NonFinal or @PackagePrivate annotations.
It is possible to override any default behavior for any of the 'parts' that make up @Value by explicitly using that annotation.

With Lombok

01 import lombok.AccessLevel;
02 import lombok.experimental.NonFinal;
03 import lombok.experimental.Value;
04 import lombok.experimental.Wither;
05 import lombok.ToString;
06 
07 @Value public class ValueExample {
08   String name;
09   @Wither(AccessLevel.PACKAGE@NonFinal int age;
10   double score;
11   protected String[] tags;
12   
13   @ToString(includeFieldNames=true)
14   @Value(staticConstructor="of")
15   public static class Exercise<T> {
16     String name;
17     T value;
18   }
19 }

Vanilla Java

001 import java.util.Arrays;
002 
003 public final class ValueExample {
004   private final String name;
005   private int age;
006   private final double score;
007   protected final String[] tags;
008   
009   @java.beans.ConstructorProperties({"name""age""score""tags"})
010   public ValueExample(String name, int age, double score, String[] tags) {
011     this.name = name;
012     this.age = age;
013     this.score = score;
014     this.tags = tags;
015   }
016   
017   public String getName() {
018     return this.name;
019   }
020   
021   public int getAge() {
022     return this.age;
023   }
024   
025   public double getScore() {
026     return this.score;
027   }
028   
029   public String[] getTags() {
030     return this.tags;
031   }
032   
033   @java.lang.Override
034   public boolean equals(Object o) {
035     if (o == thisreturn true;
036     if (!(instanceof ValueExample)) return false;
037     final ValueExample other = (ValueExample)o;
038     final Object this$name = this.getName();
039     final Object other$name = other.getName();
040     if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
041     if (this.getAge() != other.getAge()) return false;
042     if (Double.compare(this.getScore(), other.getScore()) != 0return false;
043     if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
044     return true;
045   }
046   
047   @java.lang.Override
048   public int hashCode() {
049     final int PRIME = 59;
050     int result = 1;
051     final Object $name = this.getName();
052     result = result * PRIME + ($name == null 43 : $name.hashCode());
053     result = result * PRIME + this.getAge();
054     final long $score = Double.doubleToLongBits(this.getScore());
055     result = result * PRIME + (int)($score >>> 32 ^ $score);
056     result = result * PRIME + Arrays.deepHashCode(this.getTags());
057     return result;
058   }
059   
060   @java.lang.Override
061   public String toString() {
062     return "ValueExample(name=" + getName() ", age=" + getAge() ", score=" + getScore() ", tags=" + Arrays.deepToString(getTags()) ")";
063   }
064   
065   ValueExample withAge(int age) {
066     return this.age == age ? this new ValueExample(name, age, score, tags);
067   }
068   
069   public static final class Exercise<T> {
070     private final String name;
071     private final T value;
072     
073     private Exercise(String name, T value) {
074       this.name = name;
075       this.value = value;
076     }
077     
078     public static <T> Exercise<T> of(String name, T value) {
079       return new Exercise<T>(name, value);
080     }
081     
082     public String getName() {
083       return this.name;
084     }
085     
086     public T getValue() {
087       return this.value;
088     }
089     
090     @java.lang.Override
091     public boolean equals(Object o) {
092       if (o == thisreturn true;
093       if (!(instanceof ValueExample.Exercise)) return false;
094       final Exercise<?> other = (Exercise<?>)o;
095       final Object this$name = this.getName();
096       final Object other$name = other.getName();
097       if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
098       final Object this$value = this.getValue();
099       final Object other$value = other.getValue();
100       if (this$value == null ? other$value != null : !this$value.equals(other$value)) return false;
101       return true;
102     }
103     
104     @java.lang.Override
105     public int hashCode() {
106       final int PRIME = 59;
107       int result = 1;
108       final Object $name = this.getName();
109       result = result * PRIME + ($name == null 43 : $name.hashCode());
110       final Object $value = this.getValue();
111       result = result * PRIME + ($value == null 43 : $value.hashCode());
112       return result;
113     }
114     
115     @java.lang.Override
116     public String toString() {
117       return "ValueExample.Exercise(name=" + getName() ", value=" + getValue() ")";
118     }
119   }
120 }

Supported configuration keys:

lombok.value.flagUsage = [warning | error] (default: not set)
Lombok will flag any usage of @Value as a warning or error if configured.

Small print

Look for the documentation on the 'parts' of @Value: @ToString, @EqualsAndHashCode, @AllArgsConstructor, @FieldDefaults, and @Getter.

For classes with generics, it's useful to have a static method which serves as a constructor, because inference of generic parameters via static methods works in java6 and avoids having to use the diamond operator. While you can force this by applying an explicit @AllArgsConstructor(staticConstructor="of") annotation, there's also the @Value(staticConstructor="of") feature, which will make the generated all-arguments constructor private, and generates a public static method named of which is a wrapper around this private constructor.

@Value was an experimental feature from v0.11.4 to v0.11.9 (as @lombok.experimental.Value). It has since been moved into the core package. The old annotation is still around (and is an alias). It will eventually be removed in a future version, though.

It is not possible to use @FieldDefaults to 'undo' the private-by-default and final-by-default aspect of fields in the annotated class. Use @NonFinal and @PackagePrivate on the fields in the class to override this behaviour.