Skip to content
YellowStar5 edited this page Jul 27, 2019 · 2 revisions

@Value

让不可变类变得非常容易。

@Value 是作为 lombok v0.11.4 中的实验特征引入的。

自从 lombok v0.11.8 以来,@Value 不再暗示 @Wither

自从 lombok v0.12.0 以来,@Value 被引入到 lombok 的主包。

概览

@Value@Data 的不可变的变体;默认情况下,所有字段都是 privatefinal 的,并且都不会生成 setter。默认情况下,类本身也是 final,因为不可变性不是可以强制给子类的东西。与 @Data 一样, 有用的 toString()equals()hashCode() 方法也会生成,每个字段都会获得一个 getter 方法,并且还会生成一个覆盖每个参数的构造函数(在字段声明中初始化的 final 字段除外) 。

在实践中,@Value 等价于:final @ToString @EqualsAndHashCode @AllArgsConstructor @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @Getter,除了明确包含任何相关方法的实现只是意味着那部分不用生成并且不会发出警告。例如,如果你编写自己的 toString,则不会发生错误,并且lombok不会生成 toString。此外,任何显式构造函数,无论参数列表,都意味着lombok不会生成构造函数。如果你确实希望 lombok 生成 all-args 构造函数,请将 @AllArgsConstructor 添加到类中。你可以使用 @lombok.experimental.Tolerate 标记任何构造函数或方法,以将它们从lombok 中隐藏。

可以使用字段上的显式访问级别或使用 @NonFinal@PackagePrivate 注解来覆盖 final-by-defalut 和 private-by-default 行为。 @NonFinal 也可以在类上使用以删除 final 关键字。 通过显式使用该注解,可以覆盖构成 @Value 的任何“部分”的任何默认行为。

With Lombok

import lombok.AccessLevel;
import lombok.experimental.NonFinal;
import lombok.experimental.Value;
import lombok.experimental.Wither;
import lombok.ToString;

@Value public class ValueExample {
  String name;
  @Wither(AccessLevel.PACKAGE) @NonFinal int age;
  double score;
  protected String[] tags;
  
  @ToString(includeFieldNames=true)
  @Value(staticConstructor="of")
  public static class Exercise<T> {
    String name;
    T value;
  }
}

Vanilla Java

 import java.util.Arrays;

public final class ValueExample {
  private final String name;
  private int age;
  private final double score;
  protected final String[] tags;
  
  @java.beans.ConstructorProperties({"name", "age", "score", "tags"})
  public ValueExample(String name, int age, double score, String[] tags) {
    this.name = name;
    this.age = age;
    this.score = score;
    this.tags = tags;
  }
  
  public String getName() {
    return this.name;
  }
  
  public int getAge() {
    return this.age;
  }
  
  public double getScore() {
    return this.score;
  }
  
  public String[] getTags() {
    return this.tags;
  }
  
  @java.lang.Override
  public boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof ValueExample)) return false;
    final ValueExample other = (ValueExample)o;
    final Object this$name = this.getName();
    final Object other$name = other.getName();
    if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
    if (this.getAge() != other.getAge()) return false;
    if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
    if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
    return true;
  }
  
  @java.lang.Override
  public int hashCode() {
    final int PRIME = 59;
    int result = 1;
    final Object $name = this.getName();
    result = result * PRIME + ($name == null ? 43 : $name.hashCode());
    result = result * PRIME + this.getAge();
    final long $score = Double.doubleToLongBits(this.getScore());
    result = result * PRIME + (int)($score >>> 32 ^ $score);
    result = result * PRIME + Arrays.deepHashCode(this.getTags());
    return result;
  }
  
  @java.lang.Override
  public String toString() {
    return "ValueExample(name=" + getName() + ", age=" + getAge() + ", score=" + getScore() + ", tags=" + Arrays.deepToString(getTags()) + ")";
  }
  
  ValueExample withAge(int age) {
    return this.age == age ? this : new ValueExample(name, age, score, tags);
  }
  
  public static final class Exercise<T> {
    private final String name;
    private final T value;
    
    private Exercise(String name, T value) {
      this.name = name;
      this.value = value;
    }
    
    public static <T> Exercise<T> of(String name, T value) {
      return new Exercise<T>(name, value);
    }
    
    public String getName() {
      return this.name;
    }
    
    public T getValue() {
      return this.value;
    }
    
    @java.lang.Override
    public boolean equals(Object o) {
      if (o == this) return true;
      if (!(o instanceof ValueExample.Exercise)) return false;
      final Exercise<?> other = (Exercise<?>)o;
      final Object this$name = this.getName();
      final Object other$name = other.getName();
      if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
      final Object this$value = this.getValue();
      final Object other$value = other.getValue();
      if (this$value == null ? other$value != null : !this$value.equals(other$value)) return false;
      return true;
    }
    
    @java.lang.Override
    public int hashCode() {
      final int PRIME = 59;
      int result = 1;
      final Object $name = this.getName();
      result = result * PRIME + ($name == null ? 43 : $name.hashCode());
      final Object $value = this.getValue();
      result = result * PRIME + ($value == null ? 43 : $value.hashCode());
      return result;
    }
    
    @java.lang.Override
    public String toString() {
      return "ValueExample.Exercise(name=" + getName() + ", value=" + getValue() + ")";
    }
  }
}

支持的配置键

lombok.value.flagUsage = [ warning | error ] (默认:未设置)

如果已配置,Lombok 会将 @Value 的任何用法标记为警告或错误。

lombok.val.flagUsage = [ warning | error ] (默认:未配置)

如果已配置,Lombok 会将 val 的任何用法标记为警告或错误。

Small print

查找有关 @Value 部分的文档:@ToString@EqualsAndHashCode@AllArgsConstructor@FieldDefaults@Getter

对于具有泛型的类,使用静态方法作为构造函数是有用的,因为通过静态方法推断泛型参数在 java6 中奏效, 这样避免了必须使用菱形运算符。虽然你可以通过应用显式的@AllArgsConstructor(staticConstructor="of") 注解强制执行此操作,但还有 @Value(staticConstructor="of") 功能可供选择,它将使生成所有参数的构造函数成为私有的,并生成一个公有静态的名为 of 的方法(该方法是私有构造函数的包装器)。

各种众所周知的关于空值的注释会导致插入空值检查,并将其复制到参数中。有关详细信息,请参阅 Getter/ Setter 文档的small print.

@Value 是从 v0.11.4 到 v0.11.9 的实验性特征(如 @lombok.experimental.Value )。它已被移入核心包中。旧的注释仍然存在(并且是别名)。不过,它最终将在未来版本中删除。

无法使用 @FieldDefaults 来“撤消”带@Value注解的类中字段的 private-by-default 和 final-by-default 含义。在类中的字段上使用 @NonFinal@PackagePrivate 来覆盖此行为。

Clone this wiki locally