Lombok注解说明


  

引言:

在过往的Java项目中,充斥着太多不友好的代码:POJO的getter/setter/toString;异常处理;I/O流的关闭操作等等,这些样板代码既没有技术含量,又影响着代码的美观,Lombok应运而生。Lombok是一种Java™实用工具,可用来帮助开发人员消除Java的冗长代码,尤其是对于简单的Java对象(POJO)。它通过注解实现这一目的。通过在开发环境中实现 Lombok,开发人员可以节省构建诸如hashCode()和equals(),get()和set()这样的方法。

val

  val用在局部变量前面,相当于将变量声明为 final,但是并不常用。val 可以作为局部变量声明的类型,而不必编写实际的类型。val 可以从程序的初始化表达式中推断出类型。此功能仅适用于局部变量和 foreach 循环,不适用于字段(实体类的成员变量)。同时初始化表达式是必需的。

public static void main(String[] args) {
    val sets = new HashSet<String>();
    val lists = new ArrayList<String>();
    val maps = new HashMap<String, String>();
    //=>相当于如下
    final Set<String> sets2 = new HashSet<>();
    final List<String> lists2 = new ArrayList<>();
    final Map<String, String> maps2 = new HashMap<>();
}

@NonNull

  给方法参数增加这个注解会自动在方法内对该参数进行是否为空的校验,如果为空,则抛出NullPointerException。

public void notNullExample(@NonNull String string) {
    string.length();
}
//=>相当于
public void notNullExample(String string) {
    if (string != null) {
        string.length();
    } else {
        throw new NullPointerException("null");
    }
}

@Cleanup

  自动管理资源,用在局部变量之前,在当前变量范围内即将执行完毕退出之前会自动清理资源,自动生成try-finally这样的代码来关闭流。虽然自JDK7以来,原生引入了try–with–resource结构,但还是不如@Cleanup来的简洁。

public static void main(String[] args) {
    try {
        @Cleanup InputStream inputStream = new FileInputStream(args[0]);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    //=>相当于
    InputStream inputStream = null;
    try {
        inputStream = new FileInputStream(args[0]);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

@Getter/@Setter

  用在属性上,再也不用自己手写setter和getter方法了,还可以指定访问范围。

@Setter(AccessLevel.PUBLIC)
@Getter(AccessLevel.PROTECTED)
private int id;
private String shap;

@ToString

  用在类上,可以自动覆写toString方法,当然还可以加其他参数,例如@ToString(exclude=”id”)排除id属性,或者@ToString(callSuper=true, includeFieldNames=true)调用父类的toString方法,包含所有属性。

@ToString(exclude = "id", callSuper = true, includeFieldNames = true)
public class LombokDemo {
    private int id;
    private String name;
    private int age;
    public static void main(String[] args) {
        //输出LombokDemo(super=LombokDemo@48524010, name=null, age=0)
        System.out.println(new LombokDemo());
    }
}

@EqualsAndHashCode

  用在类上,自动生成equals方法和hashCode方法

@EqualsAndHashCode(exclude = {"id", "shape"}, callSuper = false)
public class LombokDemo {
    private int id;
    private String shap;
}

@NoArgsConstructor

  用在类上,自动生成无参构造器

@AllArgsConstructor

  用在类上,自动生成带所有属性字段的全参构造器

@RequiredArgsConstructor

  用在类上,自动生成把所有@NonNull属性作为参数的构造器

@NoArgsConstructor
@RequiredArgsConstructor(staticName = "of")//可以通过指定staticName = “of”参数,同时生成一个返回类对象的静态工厂方法
@AllArgsConstructor
public class LombokDemo {
    @NonNull
    private int id;
    @NonNull
    private String shap;
    private int age;
    public static void main(String[] args) {
        new LombokDemo(1, "circle");
        //使用静态工厂方法
        LombokDemo.of(2, "circle");
        //无参构造
        new LombokDemo();
        //包含所有参数
        new LombokDemo(1, "circle", 2);
    }
}

@Data

  注解在类上,相当于同时使用了@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstrutor这些注解,对于POJO类十分有用

@Data
public class Menu {
    private String shopId;
    private String skuMenuId;
    private String skuName;
    private String normalizeSkuName;
    private String dishMenuId;
    private String dishName;
    private String dishNum;
    //默认阈值
    private float thresHold = 0;
    //新阈值
    private float newThresHold = 0;
    //总得分
    private float totalScore = 0;
}

@Value

  用在类上,是@Data的不可变形式,相当于为属性添加final声明,只提供getter方法,而不提供setter方法

@Value
public class LombokDemo {
    @NonNull
    private int id;
    @NonNull
    private String shap;
    private int age;
    //相当于
    private final int id;
    public int getId() {
        return this.id;
    }
    ...
}

@Builder

  用在类、构造器、方法上,为你提供复杂的builder APIs,让你可以像如下方式一样调用Person.builder().name(“Adam Savage”).city(“San Francisco”).job(“Mythbusters”).job(“Unchained Reaction”).build();更多说明参考Builder

@Builder
public class BuilderExample {
    private String name;
    private int age;
    @Singular
    private Set<String> occupations;
    public static void main(String[] args) {
        BuilderExample test = BuilderExample.builder().age(11).name("test").build();
    }
}

@SneakyThrows

  自动抛受检异常,而无需显式在方法上使用throws语句

public class Test {
    @SneakyThrows()
    public void read() {
        InputStream inputStream = new FileInputStream("");
    }
    @SneakyThrows
    public void write() {
        throw new UnsupportedEncodingException();
    }
    //相当于
    public void read() throws FileNotFoundException {
        InputStream inputStream = new FileInputStream("");
    }
    public void write() throws UnsupportedEncodingException {
        throw new UnsupportedEncodingException();
    }
}

@Synchronized

  用在方法上,将方法声明为同步的。并自动加锁,而锁对象是一个私有的属性$lock或$LOCK,而java中的synchronized关键字锁对象是this或类对象,锁为this或者自己的类对象上存在副作用,就是你不能阻止非受控代码去锁this或者类对象,这可能会导致竞争条件或者其它线程错误。

public class SynchronizedDemo {
    @Synchronized
    public static void hello() {
        System.out.println("world");
    }
    //相当于
    private static final Object $LOCK = new Object[0];
    public static void hello() {
        synchronized ($LOCK) {
            System.out.println("world");
        }
    }
}

@Getter(lazy=true)

  lazy版的getter annotation, 会提高代码效率。可以在实际使用到cached的时候生成cached,同时,Lombok会自动去管理线程安全的问题,不会存在重复赋值的问题。

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

// 相当于如下所示:

import java.util.concurrent.atomic.AtomicReference;
public class GetterLazyExample {
    private final AtomicReference<java.lang.Object> cached = new AtomicReference<>();
    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对象,但是实例名称都是log,有六种可选实现类

  • @CommonsLog Creates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
  • @Log Creates log = java.util.logging.Logger.getLogger(LogExample.class.getName());
  • @Log4j Creates log = org.apache.log4j.Logger.getLogger(LogExample.class);
  • @Log4j2 Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
  • @Slf4j Creates log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
  • @XSlf4j Creates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

文章作者: YangChongZhi
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 YangChongZhi !
评论
 上一篇
JVM的符号引用和直接引用 JVM的符号引用和直接引用
   引言: 在JVM中类加载过程中,在链接(验证、准备、解析)的解析阶段,Java虚拟机会把类的二进制数据中的符号引用替换为直接引用。 一、符号引用  符号引用(Symbolic Reference)以一组符号来描述所引用的目标,符号可
2021-06-30
下一篇 
LRU缓存和LFU缓存 LRU缓存和LFU缓存
   引言: 操作系统中的页面置换算法是很多缓存机制的基础,比较经典的就有LRU和LFU算法。当缓存数据的数量未达到容量大小时,能正常存入缓存的数据结构中;当缓存的数据容量达到了最大容量,而又有新的数据需要缓存时,就得考虑如何删除已存在的缓
2021-06-28
  目录