Java 效率编码 必备插件 Lombok 让代码更优雅

本文涵盖注解

val var @NonNull @Cleanup @Getter / @Setter @ToString @EqualsAndHashCode @NoArgsConstructor @RequiredArgsConstructor @AllArgsConstructor @Data @Builder @SneakyThrows @Synchronized @Wither @Getter(lazy=true) @Log 持续更新...

什么是 Lombok

Lombok项目是一种自动接通你的编辑器和构建工具的一个Java库,它会自动插入您的编辑器和构建工具中,永远不会再写getter或equals方法,自动化您的日志记录变量等等。当我们实体中定义的属性发生类型、属性名改变时,通过Lombok注解生成的方法会自动改变,不用编码人员关注修改

为什么要使用 Lombok

根据实际使用情况来看,个人觉得有以下几点

  • 极大程度的提高了编码效率
  • 对于部分冗余代码做出了优化
  • 代码会优雅一些(个人觉得这点很重要)

IDEA 安装Lombock 插件

Windows

file -> Settings -> Plugins

Mac OS

Preferences -> Plugins 或 快捷键 Command + ,

搜索Lombock插件进行下载重启IDEA

pom文件中引用Lombok的maven坐标

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
</dependency>

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");
    }
}

experimental

传送门

Lombock官网

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351

推荐阅读更多精彩内容

  • Lombok:让JAVA代码更优雅关于Lombok,其实在网上可以找到很多如何使用的文章,但是很少能找到比较齐全的...
    it_zzy阅读 1,654评论 1 8
  • lombok 简化 Java 代码 1.介绍 Lombok 是一种 Java 实用工具,可用来帮助开发人员消除 J...
    tojian阅读 514评论 0 0
  • 蝶恋花·柳岸依然花弄影 横野青青春满径,流水淙淙,旖旎风光映。 柳岸依然花弄影,穿帘紫燕双交颈。 红杏一枝墙外并,...
    不语不问阅读 107评论 0 1
  • 转眼时间进入腊月二十九了,准确的说,明天就是年三十除夕夜了,还是有些期待。 在二十八日,我的计划是二十九回家,因为...
    闲梦清秋阅读 489评论 0 6
  • 你想要的,生活都会给你。 你相信吸引力法则吗? 你相信努力的意义吗? 你还相信相信的力量吗? 时至今日,每个人都走...
    回忆里的蒲公英阅读 390评论 0 0