美文网首页Java 杂谈
Java 效率编码 必备插件 Lombok 让代码更优雅

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

作者: 是龙台呀 | 来源:发表于2019-09-25 07:56 被阅读0次

    本文涵盖注解

    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官网

    相关文章

      网友评论

        本文标题:Java 效率编码 必备插件 Lombok 让代码更优雅

        本文链接:https://www.haomeiwen.com/subject/irnuuctx.html