极简代码神器:Lombok使用教程

简介: 极简代码神器:Lombok使用教程

(点击即可跳转阅读)


1. SpringBoot内容聚合

2. 面试题内容聚合

3. 设计模式内容聚合

4. 排序算法内容聚合

5. 多线程内容聚合


Lombok 是一个非常神奇的 java 类库,会利用注解自动生成 java Bean 中烦人的 Getter、Setter,还能自动生成 logger、ToString、HashCode、Builder 等 java特色的函数或是符合设计模式的函数,能够让你 java Bean 更简洁,更美观。


lombok 的思想非常先进,它让我们省略繁琐的样板代码,不要在重复的代码上花费太长时间,它也是Java语言演进过程中必然出现的一种思想,要用20% 的时间做 80%的事情。


下面就来看一下 lombok 的具体用法。


@Data


@Data 是一个很方便的注解,它和@ToString@EqualAndHashCode@Getter/@Setter、和@RequiredArgsConstructor 绑定在一起。换句话说,@Data 生成通常与简单的POJO(Plain Old Java Objects) 和 bean 相关联的所有样板代码,例如:获取所有的属性,设置所有不可继承的属性,适应toString、equals 和 hashcode 的实现,通过构造方法初始化所有final 的属性,以及所有没有使用@NonNull标记的初始化程序的非final字段,以确保该字段永远不为null。


@Data 就像在类上隐含使用 @toString 、 @EqualAndHashCode、 @Getter、


@Setter 和 @RequiredArgsConstructor 注释一样。@Data = @Getter + @Setter +


@ToString + @EqualsAndHashCode + @RequiredArgsConstructor


但是,@Data 无法设置这些注解的参数,例如callSuper、includeFieldNames 和 exclude


如果您需要为这些参数中的任何一个设置非默认值,只需显式添加这些注释;


生成的所有getters/setters 默认都是public 的,为了覆盖访问级别,请使用显式的@Setter \ @Getter批注对字段或类进行注释。你可以使用这个注释(通过与 AccessLevel.NONE结合)来禁止使用 getter或setter。


所有使用 transient 标记的字段都不会视为 hashcode 和 equals。将完全跳过所有静态字段(不考虑任何生成的方法,并且不会为它们创建setter / getter)。


如果类已经包含与通常生成的任何方法具有相同名称和参数计数的方法,则不会生成该方法,也不会发出警告或错误。


例如:如果你使用 equals 标记了一个方法,那么不会再生成 equals 方法,即使从技术上讲,由于具有不同的参数类型,它可能是完全不同的方法。同样的规则适用于构造函数(任何显式构造函数都会阻止 @Data 生成一个),以及toString,equals和所有getter和setter。


您可以使用@ lombok.experimental.Tolerate 标记任何构造函数或方法,以将它们隐藏在 lombok 中


例如:

import lombok.AccessLevel;
import lombok.Data;
import lombok.Setter;
import lombok.ToString;
@Data
public class DataExample {
    private final String name;
    @Setter(AccessLevel.PACKAGE)
    private int age;
    private double score;
    private String[] tags;
    @ToString(includeFieldNames = true)
    @Data(staticConstructor = "of")
    public static class Exercise<T> {
        private final String name;
        private final T value;
    }
}


就相当于是不用 lombok 的如下示例:

import java.util.Arrays;
public class DataExample {
  private final String name;
  private int age;
  private double score;
  private String[] tags;
  public DataExample(String name) {
    this.name = name;
  }
  public String getName() {
    return this.name;
  }
  void setAge(int age) {
    this.age = age;
  }
  public int getAge() {
    return this.age;
  }
  public void setScore(double score) {
    this.score = score;
  }
  public double getScore() {
    return this.score;
  }
  public String[] getTags() {
    return this.tags;
  }
  public void setTags(String[] tags) {
    this.tags = tags;
  }
  @Override public String toString() {
    return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
  }
  protected boolean canEqual(Object other) {
    return other instanceof DataExample;
  }
  @Override public boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof DataExample)) return false;
    DataExample other = (DataExample) o;
    if (!other.canEqual((Object)this)) return false;
    if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) 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;
  }
  @Override public int hashCode() {
    final int PRIME = 59;
    int result = 1;
    final long temp1 = Double.doubleToLongBits(this.getScore());
    result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
    result = (result*PRIME) + this.getAge();
    result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
    result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
    return result;
  }
  public static 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;
    }
    @Override public String toString() {
      return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
    }
    protected boolean canEqual(Object other) {
      return other instanceof Exercise;
    }
    @Override public boolean equals(Object o) {
      if (o == this) return true;
      if (!(o instanceof Exercise)) return false;
      Exercise<?> other = (Exercise<?>) o;
      if (!other.canEqual((Object)this)) return false;
      if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
      if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
      return true;
    }
    @Override public int hashCode() {
      final int PRIME = 59;
      int result = 1;
      result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
      result = (result*PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
      return result;
    }
  }
}


@NonNull


你可以使用 @NonNull 对方法或者构造器生成 null - check

如果lombok为您生成整个方法或构造函数(例如@Data),Lombok总是将字段上通常称为@NonNull的各种注释视为生成空值检查的信号。但是,现在,在参数上使用lombok自己的@lombok.NonNull会导致在您自己的方法或构造函数中只插入null-check语句。


Null - Check 语句看起来像是如下语句

if(param == null){
  throw new NullPointerException("param is marked @NonNull but is null")
}


这条判空语句会在方法的最开始进行判断


public class NonNullExample {
    @Getter
    private String name;
    public NonNullExample(@NonNull String name){
        this.name = name;
    }
}


这个加上 @NonNull 判空的代码就相当于如下代码


import lombok.NonNull;
public class NonNullExample {
  private String name;
  public NonNullExample(@NonNull String name) {
    if (name == null) {
      throw new NullPointerException("name is marked @NonNull but is null");
    }
    this.name = name;
  }
}


@Getter & @Setter


你可以使用 @Getter 和 @Setter 自动生成任何 getter/setter。


默认的 getter 只返回字段的名称,如果字段的名称为 foo,则返回的是 getFoo(),如果字段类型为 boolean ,则返回 isFoo()。如果字段为 foo 的话,默认的 setter 返回 setFoo,并且类型是 void ,并且带有一个和该属性相同的字段作为参数,用于为此属性字段进行赋值。


除非你指定AccessLevel 访问级别,否则使用 Getter / Setter 生成的方法默认是 public 的作用范围。AccessLevel的访问级别有 PUBLIC, PROTECTED, PACKAGE, and PRIVATE.


你也可以在类上使用 @Getter / @Setter ,在这种情况下,就会对该类中的所有非静态属性生成 get and set 方法


你也可以通过设置 AccessLevel.NONE 禁用任何 get and set 方法的生成。这会使 @Data、@Getter / @Setter 的注解失效。


public class GetterSetterExample {
    @Setter
    @Getter
    private int age = 10;
    @Setter(AccessLevel.PROTECTED)
    private String name;
    @Getter(AccessLevel.PRIVATE)
    private String high;
}


就等同于


public class GetterSetterExample {
  private int age = 10;
  private String name;
  private String high;
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  protected void setName(String name) {
    this.name = name;
  }
  private String getHigh(){
    return high;
  }
}


@ToString


@ToString 注解用来替换掉生成 toString() 方法的实现,默认情况下,它会按顺序打印你的班级名称以及每个字段,并以逗号分隔。


通过设置 includeFieldNames = true 能够使 toString() 方法打印每个字段的属性值和名称。


默认情况下,所有非静态属性都被打印,如果你想要排除某些字段的话,需要设置 @ToString.Exclude,或者,你可以指定ToString(onlyExplicitlyIncluded = true)来指定哪些你希望使用的字段。然后使用@ ToString.Include标记要包含的每个字段。


通过设置 callSuper 为 true ,可以将toString的超类实现的输出包含到输出中。请注意,java.lang.Object 的 toString() 实现没有任何意义,所以你可能不会这样做除非你想要扩展另一个类。


你还可以在toString 中包含方法调用的输出。只能包含不带参数的实例(非静态)方法,为此,请使用@ ToString.Include标记方法。


你可以使用 @ToString.Include(name =“some other name”)更改用于标识成员的名称,并且可以通过 @ ToString.Include(rank = -1)更改成员的打印顺序。没有定义等级的成员默认是0级,等级高的成员优先被打印,优先级相同的成员按照它们在源文件中出现的顺序打印。


@ToString
public class ToStringExample {
  // 静态属性不会包含
  private static final int STATIC_VAR = 10;
  private String name;
  private String[] tags;
  private Shape shape = new Square(5, 10);
  // 排除指定字段不会被 toString 打印
  @ToString.Exclude
  private int id;
  public String getName() {
    return this.name;
  }
  // callSuper 表示是否扩展父类的 toString(), 
  // includeFieldNames 表示是否包含属性名称
  @ToString(callSuper = true, includeFieldNames = true)
  public static class Square extends Shape{
    private final int width, height;
    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }
  }
  public static class Shape {}
}


测试一下上面的示例


ToStringExample toStringExample = new ToStringExample();
System.out.println(toStringExample);


输出如下


ToStringExample(name=null, tags=null, shape=ToStringExample.Square(super=com.project.lombok.ToStringExample$Square@1b9e1916, width=5, height=10))


注释掉 callSuper = true, 测试结果如下


ToStringExample(name=null, tags=null, shape=ToStringExample.Square(width=5, height=10))


从输出可以看出,如果不扩展父类,不会输出关于 Shape 的内部类信息,callSuper 默认为 false


注释掉 includeFieldNames,测试结果不会发生变化,所以 includeFieldNames 默认值为 true


更改 includeFieldNames = false,测试结果如下


ToStringExample(name=null, tags=null, shape=ToStringExample.Square(super=com.project.lombok.ToStringExample$Square@1b9e1916, 5, 10))


从输出可以看出,如果设置 includeFieldNames = false ,不会输出Shape 中的字段名称信息。


上面用@ToString 注解修饰的例子就相当于是下面这段代码


import java.util.Arrays;
public class ToStringExample {
  private static final int STATIC_VAR = 10;
  private String name;
  private Shape shape = new Square(5, 10);
  private String[] tags;
  private int id;
  public String getName() {
    return this.getName();
  }
  public static class Square extends Shape {
    private final int width, height;
    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }
    @Override public String toString() {
      return "Square(super=" + super.toString() + ", width=" + this.width + ", height=" + this.height + ")";
    }
  }
  @Override public String toString() {
    return "ToStringExample(" + this.getName() + ", " + this.shape + ", " + Arrays.deepToString(this.tags) + ")";
  }
  public static class Shape {}
}


@EqualsAndHashCode


任何类的定义都可以用@EqualsAndHashCode 标注,让 lombok 为其生成 equalshashCode 方法。默认情况下,将会用在非静态,非 transient 标记的字段上,但是你可以通过 @EqualsAndHashCode.Include@EqualsAndHashCode.Exclude 标记类型成员来修改使用哪些字段。


或者,你可以通过使用 @EqualsAndHashCode.Include 并使用 @EqualsAndHashCode(onlyExplicitlyIncluded = true)标记它们来准确指定你希望使用的字段或方法。


如果将 @EqualsAndHashCode 应用于扩展另一个的类,这个特性就会变的很危险。通常来说,对类自动生成equalshashcode 方法不是一个好的选择,因为超类也定义了字段,这些字段也需要equals / hashCode方法。通过设置 callSuper 为 true,可以在生成的方法中包含超类的 equals 和 hachcode 方法。


对于 hashCode 来说,super.hashCode 的结果包括了哈希算法,对于 equals 来说,如果超类实现认为它不等于传入的对象,生成的方法将返回 false。请注意,不是所有的equals 实现都能正确处理这种情况。然而,lombok生成的 equals实现可以正确处理这种情况。


如果不扩展类时(只扩展任何java.lang.Object 类)时把 callSuper 设置为 true 会提示编译错误,因为 lombok 会将生成的 equals() 方法和 hashCode() 实现转换为从 Object 继承过来:只有相同的 Object 对象彼此相等并且具有相同的 hashCode 。


当你继承其他类时没有设置 callSuper 为 true 会进行警告,因为除非父类没有相同的属性,lombok无法为您生成考虑超类声明的字段的实现。你需要自己写实现类或者依赖 callSuper 工具。你还可以使用 lombok.equalsAndHashCode.callSuper 配置key。

下面是一个例子



@EqualsAndHashCode
public class EqualsAndHashCodeExample {
    private transient int transientVar = 10;
    private String name;
    private double score;
    @EqualsAndHashCode.Exclude private Shape shape = new Square(5,10);
    private String[] tags;
    @EqualsAndHashCode.Exclude private int id;
    public String getName() {
        return name;
    }
    @EqualsAndHashCode(callSuper = true)
    public static class Square extends Shape {
        private final int width,height;
        public Square(int width,int height){
            this.width = width;
            this.height = height;
        }
    }
    public static class Shape {}
}


@NoArgsConstructor,

@RequiredArgsConstructor,

@AllArgsConstructor


lombok 有三个生成构造函数的注解,下面一起来看一下它们的使用说明和示例

@NoArgsConstructor 将会生成无参数的构造函数,如果有final 修饰的字段并且没有为 final 修饰的字段进行初始化的话,那么单纯的使用 @NoArgsConstructor 注解会提示编译错误


image.png


修改建议:需要为 @NoArgsConstructor 指定一个属性

@NoArgsConstructor(force=true),lombok会为上面的final 字段默认添加初始值,因为id 是 int 类型,所以 id 的初始值为 0,类似的不同类型的字段的初始值还有 false / null / 0,特定的 Java 构造,像是 hibernate 和 服务提供接口需要无参数的构造方法。此注解主要与 @Data 或生成注解的其他构造函数组合使用。


这里有个需要注意的地方:@NonNull 不要和 @NoArgsConstructor 一起使用

@NoArgsConstructor
@Getter
public class NoArgsConstructorExample {
    private  Long id ;
    private @NonNull String name;
    private Integer age;
    public static void main(String[] args) {
        System.out.println(new NoArgsConstructorExample().getName());
    }
}


输出结果是 null ,因此如果有 @NonNull 修饰的成员的变量就不要用 @NoArgsConstructor 修饰类


@RequiredArgsConstructor 将为每个需要特殊处理的字段生成一个带有1个参数的构造函数。所有未初始化的 final 字段都会获取一个参数,以及标记为 @NonNull 的任何字段也会获取一个参数。这些字段在声明它们的地方没有初始化。对于这些标记为 @NonNull 的字段,会生成特殊的null 编译检查。如果标记为 @NonNull 的字段的参数为 null,那么构造函数将会抛出 NullPointerException。参数的顺序与字段在类中的显示顺序相匹配。


例如下面这个例子,只有 @NonNull 和 final 修饰的字段才会加入构造函数


@RequiredArgsConstructor
public class RequiredArgsConstructorExample {
    @NonNull
    private int id;
    private final String name;
    private boolean human;
}


生成的结果大概是这样的


public class RequiredArgsConstructorExample {
    @NonNull
    private int id;
    private final String name;
    private boolean human;
    public RequiredArgsConstructorExample(@NonNull int id, String name) {
        if (id == null) {
            throw new NullPointerException("id is marked @NonNull but is null");
        } else {
            this.id = id;
            this.name = name;
        }
    }
}


@AllArgsConstructor: @AllArgsConstructor 为类中的每个字段生成一个带有1个参数的构造函数。标有@NonNull 的字段会导致对这些参数进行空检查。


@AllArgsConstructor
public class AllArgsConstructorExample {
    private int id;
    private String name;
    private int age;
}


相当于自动生成如下代码


public AllArgsConstructorExample(int id, String name, int age) {
    this.id = id;
    this.name = name;
    this.age = age;
}


这些注解中的每一个都允许使用替代形式,其中生成的构造函数始终是私有的,并且生成包含私有构造函数的附加静态工厂方法,通过为注释提供staticName值来启用此模式,

@RequiredArgsConstructor(staticName =“of”)。看下面这个例子


@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> {
  private int x, y;
  @NonNull private T description;
  @NoArgsConstructor
  public static class NoArgsExample {
    @NonNull private String field;
  }
}


就会变为


public class ConstructorExample<T> {
  private int x, y;
  @NonNull private T description;
  private ConstructorExample(T description) {
    if (description == null) throw new NullPointerException("description");
    this.description = description;
  }
  public static <T> ConstructorExample<T> of(T description) {
    return new ConstructorExample<T>(description);
  }
  @java.beans.ConstructorProperties({"x", "y", "description"})
  protected ConstructorExample(int x, int y, T description) {
    if (description == null) throw new NullPointerException("description");
    this.x = x;
    this.y = y;
    this.description = description;
  }
  public static class NoArgsExample {
    @NonNull private String field;
    public NoArgsExample() {
    }
  }
}


文章参考:

https://www.hellojava.com/a/74973.html

https://www.projectlombok.org/features/constructor

目录
相关文章
|
2天前
|
设计模式 Java
Java设计模式:组合模式的介绍及代码演示
组合模式是一种结构型设计模式,用于将多个对象组织成树形结构,并统一处理所有对象。例如,统计公司总人数时,可先统计各部门人数再求和。该模式包括一个通用接口、表示节点的类及其实现类。通过树形结构和节点的通用方法,组合模式使程序更易扩展和维护。
Java设计模式:组合模式的介绍及代码演示
|
1天前
|
Java API
Java时间戳教程
本文详细介绍Java中时间戳的处理方法,包括获取当前时间戳、使用`java.time`包、时间戳与日期的相互转换及格式化等。示例代码展示了如何利用`System.currentTimeMillis()`和`java.time.Instant`获取时间戳,以及如何通过`Date`和`ZonedDateTime`进行日期转换和时区处理。随着Java 8引入的`java.time`包,日期时间操作变得更加强大和便捷,推荐在新项目中优先采用。
|
2天前
|
Java 程序员 API
Java中的Lambda表达式:简化代码的秘密武器
在Java 8中引入的Lambda表达式是一种强大的编程工具,它可以显著简化代码,提高可读性。本文将介绍Lambda表达式的基本概念、优势以及在实际开发中的应用。通过具体示例,您将了解如何使用Lambda表达式来简化集合操作、线程编程和函数式编程。让我们一起探索这一革命性的特性,看看它是如何改变Java编程方式的。
15 4
|
2天前
|
Java 开发者
探索Java中的Lambda表达式:简化你的代码
【8月更文挑战第49天】在Java 8的发布中,Lambda表达式无疑是最令人兴奋的新特性之一。它不仅为Java开发者提供了一种更加简洁、灵活的编程方式,而且还极大地提高了代码的可读性和开发效率。本文将通过实际代码示例,展示如何利用Lambda表达式优化和重构Java代码,让你的编程之旅更加轻松愉快。
|
7天前
|
SQL JavaScript 前端开发
基于Java访问Hive的JUnit5测试代码实现
根据《用Java、Python来开发Hive应用》一文,建立了使用Java、来开发Hive应用的方法,产生的代码如下
28 6
|
5天前
|
Java 开发者
探索Java中的Lambda表达式:简化代码,提升效率
【9月更文挑战第14天】本文旨在揭示Java 8中引入的Lambda表达式如何革新了我们编写和管理代码的方式。通过简洁明了的语言和直观的代码示例,我们将一起走进Lambda表达式的世界,了解其基本概念、语法结构以及在实际编程中的应用。文章不仅会展示Lambda表达式的魅力所在,还会指导读者如何在日常工作中有效利用这一特性,以提高编码效率和程序可读性。
|
11天前
|
并行计算 Java 开发者
探索Java中的Lambda表达式:简化代码,提升效率
Lambda表达式在Java 8中引入,旨在简化集合操作和并行计算。本文将通过浅显易懂的语言,带你了解Lambda表达式的基本概念、语法结构,并通过实例展示如何在Java项目中应用Lambda表达式来优化代码,提高开发效率。我们将一起探讨这一现代编程工具如何改变我们的Java编码方式,并思考它对程序设计哲学的影响。
|
IDE Java API
Lombok:让JAVA代码更优雅
Lombok:让JAVA代码更优雅
100 0
Lombok:让JAVA代码更优雅
|
Java 开发工具 IDE
|
Java 开发工具 IDE
lombok 简化java代码注解
lombok 简化java代码注解 安装lombok插件 以intellij ide为例 File-->Setting-->Plugins-->搜索“lombok plugin”,安装后重启ide lombok 注解 lombok 提供的注解不多,可以参考官方视频的讲解和官方文档。
857 0