Lombok使用详解

·  阅读 207

Lombok是一个Java库,能自动插入编辑器并构建工具,简化Java开发。本文主要介绍lombok的使用。

以前的Java项目中,充斥着太多不友好的代码:POJO的getter/setter/toString;异常处理;I/O流的关闭操作等等,这些样的代码既没有技术含量,又影响着代码的美观。

一、准备

使用Lombok需要的开发环境Java+Maven+IntelliJ IDEA并安装 Lombok 插件

1. 添加依赖

maven仓库Lombok依赖:mvnrepository.com/artifact/or…

例如,1.18.8版本:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
    <scope>provided</scope>
</dependency>
复制代码

2. IDEA中添加插件

Settings --> Plugins 中搜索 Lombok 安装即可,安装完毕后重启IDEA。使用Lombok必须要添加插件。

在这里插入图片描述

二、原理

自从Java 6起,javac就支持“JSR 269 Pluggable Annotation Processing API”规范,只要程序实现了该API,就能在javac运行的时候得到调用。

Lombok就是一个实现了"JSR 269 API"的程序。在使用javac的过程中,它产生作用的具体流程如下:

  1. javac对源代码进行分析,生成一棵抽象语法树(AST)

  2. javac编译过程中调用实现了JSR 269的Lombok程序

  3. 此时Lombok就对第一步骤得到的AST进行处理,找到Lombok注解所在类对应的语法树 (AST),然后修改该语法树(AST),增加Lombok注解定义的相应树节点

  4. javac使用修改后的抽象语法树(AST)生成字节码文件

更为详细的原理:Java奇技淫巧-插件化注解处理API(Pluggable Annotation Processing API)

三、使用

1. @val

@val用于声明修饰符为final的局部变量类型,使用的时候不需要编写实际的类型,编译器将从设定的初始值推断出变量类型。

注意:

  • 此功能仅适用于局部变量foreach循环,而不适用于字段。
  • 被修饰的局部变量必须设定初始值

(1)使用Lombok


import java.util.ArrayList;
import java.util.HashMap;
import lombok.val;

public class ValExample {
	public String example() {
		val example = new ArrayList<String>();
		example.add("Hello, World!");
		val foo = example.get(0);
		return foo.toLowerCase();
	}
  
	public void example2() {
		val map = new HashMap<Integer, String>();
		map.put(0, "zero");
		map.put(5, "five");
		for (val entry : map.entrySet()) {
			System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
		}
	}
}
复制代码

(2)未使用Lombok


import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class ValExample {
	public String example() {
		final ArrayList<String> example = new ArrayList<String>();
		example.add("Hello, World!");
		final String foo = example.get(0);
		return foo.toLowerCase();
	}
  
	public void example2() {
		final HashMap<Integer, String> map = new HashMap<Integer, String>();
		map.put(0, "zero");
		map.put(5, "five");
		for (final Map.Entry<Integer, String> entry : map.entrySet()) {
			System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
		}
	}
}
复制代码

2. @var

@var的功能类似于@val,不过@var修饰的是非final修饰的局部变量

3. @NonNull(判空)

@NonNull的作用是,使方法或构造函数中的参数生成一个空检查语句。基本格式是:if (param == null) throw new NullPointerException("param is marked @NonNull but is null");

注意:

  • 空检查将插入到方法的最顶部。
  • 对于构造函数,将在任何显式调用this()或super()之后立即插入空检查。
  • 如果顶部已经存在空检查,则不会生成其他空检查。

(1)使用Lombok


import lombok.NonNull;

public class NonNullExample extends Something {
	private String name;
	
	public NonNullExample(@NonNull Person person) {
		super("Hello");
		this.name = person.getName();
	}
}
复制代码

(2)未使用Lombok

import lombok.NonNull;

public class NonNullExample extends Something {
	private String name;
	
	public NonNullExample(@NonNull Person person) {
		super("Hello");
		if (person == null) {
			throw new NullPointerException("person is marked @NonNull but is null");
		}
		this.name = person.getName();
	}
}
复制代码

4. @Cleanup(自动资源关闭)

使用@Cleanup修饰的变量,可以在变量退出当前作用域范围之前,自动清除给定资源。使用方法:

@Cleanup InputStream in = new FileInputStream("some/file");
复制代码

这样,将在变量作用域的末尾调用in.close()方法。该调用会通过try / finally运行,并进行判空。

注意:

  • 如果您要清除的对象类型没有close()方法,而是其他无参数方法,则可以像下面这样指定该方法的名称:

    @Cleanup("dispose") org.eclipse.swt.widgets.CoolBar bar = new CoolBar(parent, 0);
    复制代码
  • 默认情况下,假定清除方法为close()。@Cleanup不能调用带有1个或多个参数的清理方法。

(1)使用Lombok

import lombok.Cleanup;
import java.io.*;

public class CleanupExample {
	public static void main(String[] args) throws IOException {
	@Cleanup InputStream in = new FileInputStream(args[0]);
	@Cleanup OutputStream out = new FileOutputStream(args[1]);
	byte[] b = new byte[10000];
	while (true) {
		int r = in.read(b);
		if (r == -1) break;
		out.write(b, 0, r);
		}
	}
}
复制代码

(2)未使用Lombok

import java.io.*;

public class CleanupExample {
	public static void main(String[] args) throws IOException {
		InputStream in = new FileInputStream(args[0]);
		try {
			OutputStream out = new FileOutputStream(args[1]);
			try {
				byte[] b = new byte[10000];
				while (true) {
					int r = in.read(b);
					if (r == -1) break;
					out.write(b, 0, r);
				}
			} finally {
			    if (out != null) {
					out.close();
			    }
			}
		} finally {
			if (in != null) {
				in.close();
			}
		}
	}
}
复制代码

5. @Getter和@Setter(生成Getter和Setter)

使用@Getter@Setter修饰任何字段,lombok会自动生成默认的getter / setter

注意:

  • 默认的getter命名:字段名为foo时,getter命名为getFoo,如果字段的类型为boolean时,方法名为isFoo
  • 默认的setter:当字段名为foo时,setter的方法名为setFoo,返回值默认为void,形参只有一个,类型与字段相同。
  • 默认生成的方法的访问权限为public,可以指定一个AccessLevel(见例子中最后一个字段),访问级别PUBLIC,PROTECTED,PACKAGE,PRIVATE;NONE为禁用对应的getter或者setter的生成。
  • 在类上添加@Getter或@Setter注释,表示该类中的所有非静态字段均生成getter和setter。
  • lombok v1.12.0中的新增功能:字段上的javadoc将被复制到生成的getter和setter中。通常情况下,所有的文字都会被复制,且@return被移动到getter,而@param被移动到setter。

(1)使用Lombok

import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;

public class GetterSetterExample {
    /**
     * Age of the person. Water is wet.
     *
     * @param age New value for this person's age. Sky is blue.
     * @return The current value of this person's age. Circles are round.
     */
    @Getter
    @Setter
    private int age = 10;

    /**
     * Name of the person.
     * -- SETTER --
     * Changes the name of this person.
     *
     * @param name The new value.
     */
    @Setter(AccessLevel.PROTECTED)
    private String name;

    @Override
    public String toString() {
        return String.format("%s (age: %d)", name, age);
    }
}
复制代码

(2)未使用Lombok

public class GetterSetterExample {
    /**
     * Age of the person. Water is wet.
     */
    private int age = 10;

    /**
     * Name of the person.
     */
    private String name;

    @Override
    public String toString() {
        return String.format("%s (age: %d)", name, age);
    }

    /**
     * Age of the person. Water is wet.
     *
     * @return The current value of this person's age. Circles are round.
     */
    public int getAge() {
        return age;
    }

    /**
     * Age of the person. Water is wet.
     *
     * @param age New value for this person's age. Sky is blue.
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * Changes the name of this person.
     *
     * @param name The new value.
     */
    protected void setName(String name) {
        this.name = name;
    }
}
复制代码

6. @ToString(生成ToString)

使用@ToString修饰类,生成该类的toString()方法。默认情况下,它将按顺序打印Class名称以及每个字段的名称,并以逗号分隔。

  • includeFieldNames参数设置为true(默认为false),toString()方法中会额外增加每个字段的字段名。

  • 默认情况下,将打印所有非静态字段。如果要跳过某些字段,可以用@ToString.Exclude注释这些字段。另外,可以使用@ToString(onlyExplicitlyIncluded = true)精确指定要打印的字段,需要使用@ToString.Include标记每个要打印的字段。

  • 通过设置callSupertrue,可以将超类实现的 toString 包含到输出中。

  • 可以在 toString 中包含不带参数的实例(非静态)方法。使用@ToString.Include标记方法即可。

  • @ToString.Include(name = "some other name")可以用来更改 toString 方法中输出时字段的名称,

  • @ToString.Include(rank = -1)可以更改成员的打印顺序。没有指定等级的成员默认为 0,等级更高的成员首先被打印,等级相同的成员以它们在源文件中出现的顺序打印。

(1)使用Lombok

import lombok.ToString;

@ToString
public class ToStringExample {
    private static final int STATIC_VAR = 10;
    private String name;
    private Shape shape = new Square(5, 10);
    private String[] tags;
    @ToString.Exclude
    private int id;

    public String getName() {
        return this.name;
    }

    @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;
        }
    }
}
复制代码

(2)未使用Lombok

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.name;
    }

    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) + ")";
    }
}
复制代码

7. @EqualsAndHashCode

使用@EqualsAndHashCode对任何类定义进行注释,可以使lombok生成equals(Object other)hashCode()方法的实现。默认情况下,它将使用所有非静态(static)和非瞬态(transient)字段

  • 与 @ToString 类似,使用@EqualsAndHashCode.Include@EqualsAndHashCode.Exclude来标记包含哪些字段和排除哪些字段;使用@EqualsAndHashCode(onlyExplicitlyIncluded = true)只包含用@EqualsAndHashCode.Include修饰的字段
  • 如果被@EqualsAndHashCode修饰的类继承了java.lang.Object除外其它类,则需要设置@EqualsAndHashCode(callSuper=true)。换句话说,@EqualsAndHashCode 生成的 euqals 方法不会比较其父类,需要加入参数callSuper=true
  • 除非类是final的,否则lombok会生成一个canEqual方法

(1)使用Lombok

import lombok.EqualsAndHashCode;

@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 this.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;
        }
    }
}
复制代码

(2)未使用Lombok

import java.util.Arrays;

public class EqualsAndHashCodeExample {
    private transient int transientVar = 10;
    private String name;
    private double score;
    private Shape shape = new Square(5, 10);
    private String[] tags;
    private int id;

    public String getName() {
        return this.name;
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof EqualsAndHashCodeExample)) return false;
        EqualsAndHashCodeExample other = (EqualsAndHashCodeExample) o;
        if (!other.canEqual((Object) this)) return false;
        if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
        if (Double.compare(this.score, other.score) != 0) return false;
        if (!Arrays.deepEquals(this.tags, other.tags)) return false;
        return true;
    }

    @Override
    public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final long temp1 = Double.doubleToLongBits(this.score);
        result = (result * PRIME) + (this.name == null ? 43 : this.name.hashCode());
        result = (result * PRIME) + (int) (temp1 ^ (temp1 >>> 32));
        result = (result * PRIME) + Arrays.deepHashCode(this.tags);
        return result;
    }

    protected boolean canEqual(Object other) {
        return other instanceof EqualsAndHashCodeExample;
    }

    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 boolean equals(Object o) {
            if (o == this) return true;
            if (!(o instanceof Square)) return false;
            Square other = (Square) o;
            if (!other.canEqual((Object) this)) return false;
            if (!super.equals(o)) return false;
            if (this.width != other.width) return false;
            if (this.height != other.height) return false;
            return true;
        }

        @Override
        public int hashCode() {
            final int PRIME = 59;
            int result = 1;
            result = (result * PRIME) + super.hashCode();
            result = (result * PRIME) + this.width;
            result = (result * PRIME) + this.height;
            return result;
        }

        protected boolean canEqual(Object other) {
            return other instanceof Square;
        }
    }
}
复制代码

8. @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor

这组的3个注解会生成一个构造函数,该构造函数将为某些字段赋值。

  • @NoArgsConstructor将生成没有参数的构造函数。如果不通生成(由于final字段),则将导致编译器错误,除非使用@NoArgsConstructor(force = true),初始化所有final字段 。对于具有约束的字段(例如,@NonNull字段),不会生成检查。该注释主要与@Data其他生成注释的构造函数之一或其中一个结合使用。

  • @RequiredArgsConstructor为每个需要处理的字段生成一个带有1个参数的构造函数。所有未初始化的字段(包括final字段和@NonNull声明的字段)均会获取参数。对于标记为的字段@NonNull,还将生成一个显式的null检查。参数的顺序与字段在类中出现的顺序匹配。

  • @AllArgsConstructor为类生成一个带有类中所有字段的构造函数。标记为@NonNull的字段将进行空检查。

  • 通过@RequiredArgsConstructor(staticName="of")来使用备用格式(以上三种注解均可使用),其中生成的构造函数是私有的,并生成基于私有构造函数的静态工厂方法。与常规构造函数不同,这种静态工厂方法将推断泛型。

(1)使用Lombok

import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.NonNull;

@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;
    }
}
复制代码

(2)未使用Lombok

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() {
        }
    }
}
复制代码

9. @Data(5,6,7,8的集合)

@Data@ToString@EqualsAndHashCode@Getter/@Setter@RequiredArgsConstructor的集合。即@Data生成与简单的POJO(Plain Old Java Object)和beans相同的模板:所有字段的getter,所有非final字段的setter和toString,equals以及hashCode,以及一个构造函数。

  • 在类上使用@Data,就像隐式的使用@Getter@Setter@ToString@EqualsAndHashCode@RequiredArgsConstructor,如果要设置这些注解的参数(如callSuper,includeFieldNames和exclude)时,不能设置@Data,而是显式添加要设置的注解即可;@Data非常聪明,可以遵循这些注释。

  • 所有生成的getter和setter将为public。

  • 如果实体类中存在 gettersetter 方法,然后又使用了 @Data 注解,则 @Data ==不会覆盖==实体类中已经存在的 gettersetter

  • 被transient修饰的所有字段不会加入hashCode和equals。

  • 所有静态字段都完全跳过(任何生成的方法都不会考虑,并且不会为它们创建setter / getter)。

  • 特别注意:生成的equals方法不会比较其父类,即不会调用父类的euqals方法,如果父类为Object对象则不用管

(1)使用Lombok

import lombok.AccessLevel;
import lombok.Setter;
import lombok.Data;
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;
    }
}
复制代码

(2)未使用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;
        }
    }
}
复制代码

10. @Value(final版的@Data)

@Value@Data类似;@Value修饰的是不可变类型(final),默认情况下类本身是final修饰的,所有字段由privatefinal修饰,每个字段会生成gettter的方法,生成一个覆盖每个参数(除了在字段声明时已经初始化了的final类型字段)的构造方法,toString()equals()hashCode()均会被生成,不会产生setter方法。

@Value相当于是:final @ToString @EqualsAndHashCode @AllArgsConstructor @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @Getter的简写

(1)使用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;
    }
}
复制代码

(2)未使用Lombok

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() + ")";
        }
    }
}
复制代码

11. @Builder(构建者模式)

@Builder的作用域是类,使用此注解后类中新增一个成员类(Builder)将会使用构建者模式,编译时增加了一个Builder内部类和全字段的构造器。@Builder.Default用于指定Builder中的属性的默认值,@Singular用于告诉lombok当前属性类型是集合类型,lombok会为信任的集合类型添加"adder"方法而不是"setter"方法。

(1)使用Lombok

import lombok.Builder;
import lombok.Singular;
import java.util.Set;

@Builder
public class BuilderExample {
    @Builder.Default
    private long created = System.currentTimeMillis();
    private String name;
    private int age;
    @Singular
    private Set<String> occupations;
}
复制代码

(2)未使用Lombok

import java.util.Set;

public class BuilderExample {
    private long created;
    private String name;
    private int age;
    private Set<String> occupations;

    BuilderExample(String name, int age, Set<String> occupations) {
        this.name = name;
        this.age = age;
        this.occupations = occupations;
    }

    private static long $default$created() {
        return System.currentTimeMillis();
    }

    public static BuilderExampleBuilder builder() {
        return new BuilderExampleBuilder();
    }

    public static class BuilderExampleBuilder {
        private long created;
        private boolean created$set;
        private String name;
        private int age;
        private java.util.ArrayList<String> occupations;

        BuilderExampleBuilder() {
        }

        public BuilderExampleBuilder created(long created) {
            this.created = created;
            this.created$set = true;
            return this;
        }

        public BuilderExampleBuilder name(String name) {
            this.name = name;
            return this;
        }

        public BuilderExampleBuilder age(int age) {
            this.age = age;
            return this;
        }

        public BuilderExampleBuilder occupation(String occupation) {
            if (this.occupations == null) {
                this.occupations = new java.util.ArrayList<String>();
            }

            this.occupations.add(occupation);
            return this;
        }

        public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
            if (this.occupations == null) {
                this.occupations = new java.util.ArrayList<String>();
            }

            this.occupations.addAll(occupations);
            return this;
        }

        public BuilderExampleBuilder clearOccupations() {
            if (this.occupations != null) {
                this.occupations.clear();
            }

            return this;
        }

        public BuilderExample build() {
            // complicated switch statement to produce a compact properly sized immutable set omitted.
            Set<String> occupations = ...;
            return new BuilderExample(created$set ? created : BuilderExample.$default$created(), name, age, occupations);
        }

        @java.lang.Override
        public String toString() {
            return "BuilderExample.BuilderExampleBuilder(created = " + this.created + ", name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
        }
    }
}
复制代码

12. @SneakyThrows(自动捕获受检异常)

@SneakyThrows的作用域是构造或者方法,用于自动捕获(隐藏)检查异常。我们知道,java对于检查异常,需要在编码时进行捕获,或者抛出。该注解的作用是将检查异常包装为运行时异常,那么编码时就无需处理异常了。

提示:不过这并不是友好的编码方式,因为你编写的api的使用者,不能显式的获知需要处理检查异常。

(1)使用Lombok

import lombok.SneakyThrows;

public class SneakyThrowsExample implements Runnable {
    @SneakyThrows(UnsupportedEncodingException.class)
    public String utf8ToString(byte[] bytes) {
        return new String(bytes, "UTF-8");
    }

    @SneakyThrows
    public void run() {
        throw new Throwable();
    }
}
复制代码

(2)未使用Lombok

import lombok.Lombok;

public class SneakyThrowsExample implements Runnable {
    public String utf8ToString(byte[] bytes) {
        try {
            return new String(bytes, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw Lombok.sneakyThrow(e);
        }
    }

    public void run() {
        try {
            throw new Throwable();
        } catch (Throwable t) {
            throw Lombok.sneakyThrow(t);
        }
    }
}
复制代码

13. @Synchronized(自动加锁)

@Synchronized的作用域是方法,用于方法同步,使用此注解后,方法体中的代码块自动包含在一个synchronize块中。synchronize块加锁的对象一定是类中的一个成员属性,可以通过@Synchronizedvalue指定,如果不存在则由lombok新建,一般是private final Object $lock = new Object[0];

(1)使用Lombok

import lombok.Synchronized;

public class SynchronizedExample {
    private final Object readLock = new Object();

    @Synchronized
    public static void hello() {
        System.out.println("world");
    }

    @Synchronized
    public int answerToLife() {
        return 42;
    }

    @Synchronized("readLock")
    public void foo() {
        System.out.println("bar");
    }
}
复制代码

(2)未使用Lombok

public class SynchronizedExample {
    private static final Object $LOCK = new Object[0];
    private final Object $lock = new Object[0];
    private final Object readLock = new Object();

    public static void hello() {
        synchronized ($LOCK) {
            System.out.println("world");
        }
    }

    public int answerToLife() {
        synchronized ($lock) {
            return 42;
        }
    }

    public void foo() {
        synchronized (readLock) {
            System.out.println("bar");
        }
    }
}
复制代码

14. @With(final字段赋值)

@With 修饰不可变字段(final),通过克隆对象的方式,对对象中不可变字段进行赋值(setter

(1)使用Lombok

import lombok.AccessLevel;
import lombok.NonNull;
import lombok.With;

public class WithExample {
    @With(AccessLevel.PROTECTED)
    @NonNull
    private final String name;
    @With
    private final int age;

    public WithExample(String name, int age) {
        if (name == null) throw new NullPointerException();
        this.name = name;
        this.age = age;
    }
}
复制代码

(2)未使用Lombok

import lombok.NonNull;

public class WithExample {
    private @NonNull final String name;
    private final int age;

    public WithExample(String name, int age) {
        if (name == null) throw new NullPointerException();
        this.name = name;
        this.age = age;
    }

    protected WithExample withName(@NonNull String name) {
        if (name == null) throw new java.lang.NullPointerException("name");
        return this.name == name ? this : new WithExample(name, age);
    }

    public WithExample withAge(int age) {
        return this.age == age ? this : new WithExample(name, age);
    }
}
复制代码

15. @Getter(lazy = true)(将字段值放入缓存)

@Getter(lazy = true)用来修饰字段,lombok生成一个该字段的getter方法,在第一次调用getter时计算字段的值,然后将其缓存。适用于计算该值占用大量CPU或内存的情况。使用方法:请创建一个private final变量,使用运行起来很昂贵的表达式对其进行初始化,然后使用@Getter(lazy=true)注释该字段。

(1)使用Lombok

import lombok.Getter;

public class GetterLazyExample {
    @Getter(lazy = true)
    private final double[] cached = expensive();

    private double[] expensive() {
        double[] result = new double[1000000];
        for (int i = 0; i < result.length; i++) {
            result[i] = Math.asin(i);
        }
        return result;
    }
}
复制代码

(2)未使用Lombok

public class GetterLazyExample {
    private final java.util.concurrent.AtomicReference<java.lang.Object> cached = new java.util.concurrent.AtomicReference<java.lang.Object>();

    public double[] getCached() {
        java.lang.Object value = this.cached.get();
        if (value == null) {
            synchronized (this.cached) {
                value = this.cached.get();
                if (value == null) {
                    final double[] actualValue = expensive();
                    value = actualValue == null ? this.cached : actualValue;
                    this.cached.set(value);
                }
            }
        }
        return (double[]) (value == this.cached ? null : value);
    }

    private double[] expensive() {
        double[] result = new double[1000000];
        for (int i = 0; i < result.length; i++) {
            result[i] = Math.asin(i);
        }
        return result;
    }
}
复制代码

16. @Log(自动生成日志对象)

@Log的作用域是类,用于生成日志API句柄。将@Log放在类上(适用于您所使用的日志系统的任何一种);然后,lombok会生成一个static final log字段,该字段按照使用的日志记录框架规定的方式进行初始化,然后可以使用该字段编写日志语句。目前支持的类型如下:

  • @CommonsLog

    private static final org.apache.commons.logging.Log log = 
    		org.apache.commons.logging.LogFactory.getLog(LogExample.class);
    复制代码
  • @Flogger

    private static final com.google.common.flogger.FluentLogger log = 
    		com.google.common.flogger.FluentLogger.forEnclosingClass();
    复制代码
  • @JBossLog

    private static final org.jboss.logging.Logger log = 
    		org.jboss.logging.Logger.getLogger(LogExample.class);
    复制代码
  • @Log

    private static final java.util.logging.Logger log = 
    		java.util.logging.Logger.getLogger(LogExample.class.getName());
    复制代码
  • @Log4j

    private static final org.apache.log4j.Logger log = 
    		org.apache.log4j.Logger.getLogger(LogExample.class);
    复制代码
  • @Log4j2

    private static final org.apache.logging.log4j.Logger log = 
    		org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
    复制代码
  • @Slf4j

    private static final org.slf4j.Logger log = 
    		org.slf4j.LoggerFactory.getLogger(LogExample.class);
    复制代码
  • @XSlf4j

    private static final org.slf4j.ext.XLogger log = 
    		org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
    复制代码
  • @CustomLog

    private static final com.foo.your.Logger log = 
    		com.foo.your.LoggerFactory.createYourLogger(LogExample.class);
    复制代码

(1)使用Lombok

import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;

@Log
public class LogExample {

    public static void main(String... args) {
        log.severe("Something's wrong here");
    }
}

@Slf4j
public class LogExampleOther {

    public static void main(String... args) {
        log.error("Something else is wrong here");
    }
}

@CommonsLog(topic = "CounterLog")
public class LogExampleCategory {

    public static void main(String... args) {
        log.error("Calling the 'CounterLog' with a message");
    }
}
复制代码

(2)未使用Lombok

public class LogExample {
    private static final java.util.logging.Logger log = 
            java.util.logging.Logger.getLogger(LogExample.class.getName());

    public static void main(String... args) {
        log.severe("Something's wrong here");
    }
}

public class LogExampleOther {
    private static final org.slf4j.Logger log = 
            org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);

    public static void main(String... args) {
        log.error("Something else is wrong here");
    }
}

public class LogExampleCategory {
    private static final org.apache.commons.logging.Log log = 
            org.apache.commons.logging.LogFactory.getLog("CounterLog");

    public static void main(String... args) {
        log.error("Calling the 'CounterLog' with a message");
    }
}
复制代码
分类:
后端
标签:
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改