1 本文涵盖注解
val
var
@NonNull
@Cleanup
@Getter / @Setter
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@RequiredArgsConstructor
@AllArgsConstructor
@Data
@Builder
@SneakyThrows
@Synchronized
@Wither
@Getter(lazy=true)
@Log
持续更新...
2 什么是 Lombok
Lombok 项目是一种自动接通你的编辑器和构建工具的一个 Java 库,它会自动插入您的编辑器和构建工具中,永远不会再写 getter
或 equals
方法,自动化您的日志记录变量等等。当我们实体中定义的属性发生类型、属性名改变时,通过 Lombok
注解生成的方法会自动改变,不用编码人员关注修改
3 为什么要使用 Lombok
根据实际使用情况来看,个人觉得有以下几点
- 极大程度的提高了编码效率
- 对于部分冗余代码做出了优化
- 代码会优雅一些(个人觉得这点很重要)
4 IDEA 安装Lombock 插件
4.1 Windows
File
->Settings
->Plugins
4.2 Mac OS
Preferences
->Plugins
或 快捷键Command + ,
搜索 Lombock
插件进行下载重启 IDEA
5 POM 文件中引用Lombok的maven坐标
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
6 实际运用
val
可以使用 val
用作局部变量声明的类型,而不必实际编写该类型
使用 val
代码
public void example1() {
val example = new ArrayList<String>();
example.add("Hello, World!");
example.forEach(value -> System.out.println(value));
}
等同于
public void example2() {
final ArrayList<String> example = new ArrayList<String>();
example.add("Hello, World!");
example.forEach(value -> System.out.println(value));
}
var
var
的工作方式和 val
是完全一样的,只不过局部变量没有标记为 final
@NonNull
@NonNull
注解防止空指针异常,可以少写很多 if
判空语句,可作用于方法入参或构造方法上
使用了 @NonNull
的代码
public void test() {
example("马马马马马百万");
}
public void example(@NonNull String name) {
System.out.println("my name is : " + name);
}
等同于
public void test() {
example("马马马马马百万");
}
public void example(String name) {
if (name == null) {
throw new NullPointerException("${parameter} is marked non-null but is null");
}
System.out.println("my name is : " + name);
}
@Cleanup
可以使用 @Cleanup
用来保障代码执行退出当前作用域时自动清楚给定资源
使用了 @Cleanup
的代码
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);
}
}
等同于
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();
}
}
}
@Getter / @Setter
@Getter / @Setter
的出现可以不再为每个字段写 get、set
方法lombok
生成的 getter / setter
方法默认作用域将是 public
除非你明确指定一个 AccessLevel
使用了 @Getter / @Setter
的代码
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
public class GetterSetterExample {
@Getter
@Setter
private int age;
@Setter(AccessLevel.PROTECTED)
private String name;
}
等同于
public class GetterSetterExample {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
protected void setName(String name) {
this.name = name;
}
}
项目实际开发中都是将 @Getter / @Setter
或者 @Data
标注在实体上的
@ToString
@ToString
以使 lombok
生成该 toString()
方法的实现@ToString.Exclude
可以将标记字段在生成的 toString()
的字段中去除 @ToString
其中还包含打印属性顺序等,不过项目中没有使用过
使用了 @ToString
的代码
import javafx.scene.shape.Shape;
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;
}
@Override
public com.sun.javafx.geom.Shape impl_configShape() {
return null;
}
}
}
等同于
import javafx.scene.shape.Shape;
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 com.sun.javafx.geom.Shape impl_configShape() {
return null;
}
}
@Override public String toString() {
return "ToStringExample(" + this.getName() + ", " + this.shape + ", " + Arrays.deepToString(this.tags) + ")";
}
}
@EqualsAndHashCode
@EqualsAndHashCode
使 lombok
生成 equals(Object other)
和 hashCode()
方法的实现
使用了 @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;
}
@Override
public com.sun.javafx.geom.Shape impl_configShape() {
return null;
}
}
}
等同于
import javafx.scene.shape.Shape;
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;
}
@Override
public com.sun.javafx.geom.Shape impl_configShape() {
return null;
}
}
@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor
@NoArgsConstructor
生成没有参数的构造函数
如果实体中包含 final
字段,将导致编译器报错,可以使用 @NoArgsConstructor(force = true)
或者初始化所有 final
字段
使用了 @NoArgsConstructor
的代码
@NoArgsConstructor
public class Student {
private Long stuNum;
private Integer age;
private String name;
}
等同于
public class Student {
private Long stuNum;
private Integer age;
private String name;
public Student() {
}
}
@RequiredArgsConstructor
为每一个需要特殊处理的字段生成带有参数的构造函数。如果实体中包含 final
字段, 那么在创建实体时必须将 final
字段作为构造入参传入
对于标有的字段 @NonNull
的字段,会生成一个 null
检查
也可以这么使用@RequiredArgsConstructor(staticName = "of")
在创建对象时 new Object.of()
使用了 @RequiredArgsConstructor
的代码
@RequiredArgsConstructor
public class Student {
@NonNull
private Long stuNum;
private Integer age;
private String name;
}
等同于
public class Student {
@NonNull
private Long stuNum;
private Integer age;
private String name;
public Student(Long stuNum) {
if (stuNum == null) {
throw new NullPointerException("xxxx");
}
}
}
@AllArgsConstructor
会为类中的每一个字段都作为构造函数的参数,如果标记 @NonNull
会进行非空检查
@Data
@Data
相当于 @ToString、@EqualsAndHashCode、@Getter、@Setter、@RequiredArgsConstructor
五个注解的融合使用
@Builder
@Builder
可以自动生成可实例化的类,例如:Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();
buider模式的代码是我个人比较喜欢的一种方式@Builder.Default
可以在构建时添加一个默认值
@ToString
@Builder
public class Student {
private Long stuNum;
private Integer age;
private String name;
@Builder.Default
private String defaultValue = "默认构造值";
public static void main(String[] args) {
System.out.println(Student.builder().name("马马马马马百万").age(26).stuNum(38593725143849L).build());
// Student(stuNum=38593725143849, age=26, name=马马马马马百万, defaultValue=默认构造值)
}
}
@SneakyThrows
介绍这个注解时,容我抛出一个梗
记得当时看 爱情公寓
的时候,吕子乔和张伟一起去酒吧泡妹子,让张伟起一个代号,张伟一口咬定 Sneaky
,当时不知道这个词啥意思,感觉挺好听, 后来才知道 偷摸、鬼鬼祟祟
@SneakyThrows
可用于将 throws
子句声明中的检查异常抛出,绝对 神不知鬼不觉
, 谨慎为妙
使用 @SneakyThrows
的代码,也可以人为指定异常类型
public class SneakyThrowsExample {
private String name;
@SneakyThrows
public static void main(String[] args) {
getNameFieldValue();
}
public static String getNameFieldValue() throws NoSuchFieldException {
return Student.class.getField("name").getName();
}
}
等同于
public class SneakyThrowsExample {
private String name;
public static void main(String[] args) {
try {
getNameFieldValue();
} catch (NoSuchFieldException e) {
Lombok.sneakyThrow(e);
}
}
public static String getNameFieldValue() throws NoSuchFieldException {
return Student.class.getField("name").getName();
}
}
@Synchronized
@Synchronized
操作类似于 synchronized
关键字,只能注释在静态方法和实例方法上
如果部署单机服务需要用到互斥锁,那么这个注解也可以起到作用。如果部署集群或分布式应用,就不会起到互斥作用,需要引入分布式锁的概念
另外,@Synchronized
锁定的作用域与 synchronized
加在方法上的作用域一致,不推荐直接加在方法上,应在使用到互斥的方法体中进行锁定。可以去了解java 关键字 synchronized
原理和使用,这里就不赘述了
使用了 @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");
}
}
相当于
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");
}
}
}
@Wither
@Wither
表示是产生克隆方法的新值
例如,如果您创建 public class Point { private final int x, y; }
,则 setter
没有意义,因为这些字段是最终字段。@Wither
可以 withX(int newXValue)
为您生成一个方法,该方法将返回一个新点,其中包含的提供的值x和的相同的值y,也可指定方法的访问级别
使用 @Wither
的代码
public class WitherExample {
@Wither(AccessLevel.PROTECTED)
@NonNull
private final String name;
@Wither
private final int age;
public WitherExample(String name, int age) {
if (name == null) throw new NullPointerException();
this.name = name;
this.age = age;
}
}
等同于
public class WitherExample {
private @NonNull final String name;
private final int age;
public WitherExample(String name, int age) {
if (name == null) throw new NullPointerException();
this.name = name;
this.age = age;
}
protected WitherExample withName(@NonNull String name) {
if (name == null) throw new java.lang.NullPointerException("name");
return this.name == name ? this : new WitherExample(name, age);
}
public WitherExample withAge(int age) {
return this.age == age ? this : new WitherExample(name, age);
}
}
@Getter(lazy=true)
@Getter(lazy=true)
lombock
生成第一次调用此 getter
时计算的值,然后将其缓存。如果计算该值占用大量CPU或改值占用大量内存,那么推荐使用此注解
如果使用此注解,那么请创建一个 private final
变量,标记 @Getter(lazy=true)
注解,在这里不必考虑线程安全问题,lombok
负责锁定该资源
使用了 @Getter(lazy=true)
的代码
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;
}
}
等同于
public class GetterLazyExample {
private final java.util.concurrent.atomic.AtomicReference<java.lang.Object> cached = new java.util.concurrent.atomic.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;
}
}
@Log
@Log
放在作用类上(适合与所使用的日志系统的任意一种),然后就会拥有一个静态的 final log
字段,该字段按照使用的日志记录框架通用规定的方式进行初始化,然后可以使用该方法编写日志语句
有很多种日志框架实现分别对应不通的 lombok
注解,这里就不一一列举了
使用了日志
相关的注解
import lombok.extern.apachecommons.CommonsLog;
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
class LogExampleOther {
public static void main(String... args) {
log.error("Something else is wrong here");
}
}
@CommonsLog(topic="CounterLog")
class LogExampleCategory {
public static void main(String... args) {
log.error("Calling the 'CounterLog' with a message");
}
}
等同于
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");
}
}
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");
}
}
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");
}
}