玩转lombok,好用的可不仅仅只有@Data

2024-08-08 15:24:21 浏览数 (3)

玩转lombok,好用的可不仅仅只有@Data

一、介绍

在开始前,我想先问问什么是lombok呢?

大家应该都听说过,它是一个Java库,通过注解的方式,帮助开发者消除Java代码中的样板代码,从而提高开发效率。

用的最熟悉的注解应该就是@Data了吧,它是一个复合注解,可以完成多个注解的功能@Getter@Setter@ToString@EqualsAndHashCode@RequiredArgsConstructor

那么这些注解都很熟悉了,下面介绍一些在lombok中不那么常用,但有时候异常好用的注解

二、注解

1)@Accessors

如下使用,可以进行链式调用

代码语言:java复制
 package com.banmoon.lombok;
 ​
 import lombok.Data;
 import lombok.experimental.Accessors;
 import org.junit.Test;
 ​
 /**
  * @author banmoon
  * @date 2024/08/08 11:09:58
  */
 public class LombokAccessorsTest {
 ​
     @Test
     public void test() {
         // 最常用的,set链式方法
         AccessorsChainBO chainBO = new AccessorsChainBO()
                                        .setId(1)
                                        .setName("banmoon")
                                        .setEnabled(true);
         System.out.println(chainBO);
     }
 ​
 }
 ​
 @Data
 @Accessors(chain = true)
 class AccessorsChainBO {
     private Integer id;
     private String name;
     private Boolean enabled;
 }

还有一个fluent属性可以实现链式调用,不过不建议开启

它会导致set前缀没有了,有许多库都是使用反射setget方法来进行的,会导致第三方库找不到方法,从而导致报错

代码语言:java复制
 package com.banmoon.lombok;
 ​
 import lombok.Data;
 import lombok.experimental.Accessors;
 import org.junit.Test;
 ​
 /**
  * @author banmoon
  * @date 2024/08/08 11:09:58
  */
 public class LombokAccessorsTest {
 ​
     @Test
     public void test() {
         // set前缀没有了,建议不要使用,有许多库都是使用反射set、get方法来进行的
         AccessorsFluentBO fluentBO = new AccessorsFluentBO()
                                               .id(1)
                                               .name("banmoon")
                                               .enabled(true);
         System.out.println(fluentBO);
     }
 ​
 }
 ​
 @Data
 @Accessors(fluent = true)
 class AccessorsFluentBO {
     private Integer id;
     private String name;
     private Boolean enabled;
 }

2)var、val

jdk10之后的版本,我们有个var的关键字,它允许开发者在声明变量时使用类型推断,使得代码更为简洁和易读。使用 var 声明变量时,编译器会根据变量的初始化表达式推断出变量的类型,从而避免显式地写出类型名称。

那么lombok中,引入了两个varval注解,同样可以达成类似的效果

对的没看错,是注解,不是关键字

如下进行使用

代码语言:java复制
 package com.banmoon.lombok;
 ​
 import lombok.val;
 import lombok.var;
 import org.junit.Test;
 ​
 import java.util.ArrayList;
 import java.util.HashMap;
 ​
 /**
  * @author banmoon
  * @date 2024/08/08 11:42:47
  */
 public class LombokVarValTest {
 ​
     @Test
     public void test() {
         var list = new ArrayList<Integer>();
         list.add(1);
         list.add(2);
         System.out.println(list);
         val map = new HashMap<String, Object>();
         map.put("name", "banmoon");
         map.put("age", 18);
         System.out.println(map);
     }
 ​
 }

同样是类型推断,varval有什么区别

  • varjdk10之后的那个关键字是一样的,仅为类型推断
  • val相比之下,多出了一个final关键字,即声明了一个不可变对象

3)@Cleanup

Java代码中,不需要关注垃圾回收,但有一个一定要主动关闭,那便是文件IO

如果使用lombok,可以快速达成关闭流的写法,如下进行使用

代码语言:java复制
 package com.banmoon.lombok;
 ​
 import lombok.Cleanup;
 import org.junit.Test;
 ​
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 ​
 /**
  * @author banmoon
  * @date 2024/08/08 12:01:12
  */
 public class LombokCleanupTest {
 ​
     @Test
     public void test() throws IOException {
         @Cleanup InputStream in = Files.newInputStream(Paths.get("/User/1.txt"));
         @Cleanup OutputStream out = Files.newOutputStream(Paths.get("/User/2.txt"));
         byte[] b = new byte[10000];
         while (true) {
             int r = in.read(b);
             if (r == -1) break;
             out.write(b, 0, r);
         }
     }
 ​
 }

其实从Java7开始,你可以使用try-with-resources语句来自动关闭实现了AutoCloseable接口的资源。这种方式确保即使在处理资源的过程中发生异常,资源也会被正确关毕

代码语言:java复制
 package com.banmoon.lombok;
 ​
 import org.junit.Test;
 ​
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 ​
 /**
  * @author banmoon
  * @date 2024/08/08 12:01:12
  */
 public class LombokCleanupTest {
 ​
     @Test
     public void test1() throws IOException {
         Path inPath = Paths.get("/User/1.txt");
         Path outPath = Paths.get("/User/2.txt");
         try (InputStream in = Files.newInputStream(inPath);
              OutputStream out = Files.newOutputStream(outPath)) {
             byte[] b = new byte[10000];
             while (true) {
                 int r = in.read(b);
                 if (r == -1) break;
                 out.write(b, 0, r);
             }
         }
     }
 ​
 }

4)@FieldNameConstants

有些时候,我们需要类的属性名去完成一些逻辑,要么老老实实的写一遍常量,要么反射类获取属性名

lombok中,使用@FieldNameConstants注解在编译的时候,就生成对应类属性的常量,如下进行使用

代码语言:java复制
 package com.banmoon.lombok;
 ​
 import cn.hutool.core.util.ReflectUtil;
 import cn.hutool.core.util.StrUtil;
 import lombok.Data;
 import lombok.experimental.FieldNameConstants;
 import org.junit.Test;
 ​
 import java.lang.reflect.Field;
 ​
 /**
  * @author banmoon
  * @date 2024/08/08 13:52:28
  */
 public class LombokFieldNameConstantsTest {
 ​
     @Test
     public void test() throws IllegalAccessException {
         FieldNameConstantsBO fieldNameConstantsBO = new FieldNameConstantsBO();
         fieldNameConstantsBO.setId(1);
         fieldNameConstantsBO.setName("banmoon");
         fieldNameConstantsBO.setEnabled(true);
         // 通过反射获取值
         Field field = ReflectUtil.getField(FieldNameConstantsBO.class, FieldNameConstantsBO.Fields.name);
         field.setAccessible(true);
         System.out.println(StrUtil.format("{}:{}", FieldNameConstantsBO.Fields.name, field.get(fieldNameConstantsBO)));
     }
 ​
 }
 ​
 @Data
 @FieldNameConstants
 class FieldNameConstantsBO {
     private Integer id;
     private String name;
     private Boolean enabled;
 }

另外,某些不需要生成常量的属性,我们可以使用lombok.experimental.FieldNameConstants.Exclude来达成

5)@SneakyThrows

在日常开发中,常常有检查异常需要显式的去处理,要么throws,要么catch。处理不说十分繁琐,总归还是有的

但在lombok中,可以使用@SneakyThrows注解来完成这个异常抛出,如下使用

代码语言:java复制
 package com.banmoon.lombok;
 ​
 import lombok.SneakyThrows;
 import org.junit.Test;
 ​
 import java.util.concurrent.TimeUnit;
 ​
 /**
  * @author banmoon
  * @date 2024/08/08 14:19:27
  */
 public class LombokSneakyThrowsTest {
 ​
     @Test
     @SneakyThrows
     public void test() {
         TimeUnit.SECONDS.sleep(1);
     }
 ​
 }

6)@Synchronized

同步锁synchronized大家都很熟悉,但lombok连这个都要简化,我感觉没必要

代码语言:java复制
 package com.banmoon.lombok;
 ​
 import lombok.Synchronized;
 ​
 /**
  * @author banmoon
  * @date 2024/08/08 15:06:11
  */
 public class LombokSynchronizedTest {
 ​
     private static final Object lock = new Object();
 ​
     @Synchronized
     public void test() {
         System.out.println(1);
     }
 ​
     @Synchronized("lock")
     public void test1() {
         System.out.println(1);
     }
 ​
     @Synchronized
     public static void staticTest() {
         System.out.println(1);
     }
 ​
 }

编译后是这个样子的

代码语言:java复制
 //
 // Source code recreated from a .class file by IntelliJ IDEA
 // (powered by FernFlower decompiler)
 //
 ​
 package com.banmoon.lombok;
 ​
 public class LombokSynchronizedTest {
     private final Object $lock = new Object[0];
     private static final Object $LOCK = new Object[0];
     private static final Object lock = new Object();
 ​
     public LombokSynchronizedTest() {
     }
 ​
     public void test() {
         synchronized(this.$lock) {
             System.out.println(1);
         }
     }
 ​
     public void test1() {
         synchronized(lock) {
             System.out.println(1);
         }
     }
 ​
     public static void staticTest() {
         synchronized($LOCK) {
             System.out.println(1);
         }
     }
 }

可以看到是同步代码块,如果没有指定value,它会自己生成一个Object

静态方法使用的话,也是使用静态的Object变量

三、最后

为什么没有@Builder,我在调试的时候出现了一点问题,这个也比较常用,需要单开一篇来解决

我是半月,另附带官网地址

Project Lombok

0 人点赞