lombok特性(二)

4 篇文章 0 订阅
3 篇文章 0 订阅

lombok特性(一)

@EqualsAndHashCode

任何类使用@EqualsAndHashCode标注生成hashCode()和equals()方法,默认情况下,它将使用所有非静态,非transient字段。但可以通过在可选的@EqualsAndHashCode.Include 或者@EqualsAndHashCode.Exclude注解字段来排除或包含指定字段

  • @EqualsAndHashCode.Exclude排除具体字段
  • @EqualsAndHashCode.Include包含指定字段,需和属性onlyExplicitlyIncluded = true配合使用
package com.demo.lombok;

import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;

@EqualsAndHashCode()
@Slf4j
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;
        }
    }
    public static void main(String[] args) {
        EqualsAndHashCodeExample equalsAndHashCodeExample = new EqualsAndHashCodeExample();
        mylog.info("equalsAndHashCodeExample.hashCode() :{}",equalsAndHashCodeExample.hashCode());
    }
}

lombok.equalsAndHashCode.doNotUseGetters设置为true,生成hashcode时lombok直接访问字段而不是访问getter,如果在注解中显式设置了doNotUseGetters参数,优先级高于配置文件中的lombok.equalsAndHashCode.doNotUseGetters配置

lombok.equalsAndHashCode.doNotUseGetters = [true | false] (default: false)

lombok.equalsAndHashCode.callSuper设置为call,如果继承了父类,lombok会实现父类的hashcode和equals,默认行为是skip,会产生一个警告

lombok.equalsAndHashCode.callSuper = [call | skip | warn] (default: warn)

可以结合lombok.config配置文件进行喜好设置:禁用、使用警告、允许(默认not set即允许)

lombok.equalsAndHashCode.flagUsage = [warning | error] (default: not set) 

@NoArgsConstructor、 @RequiredArgsConstructor 、@AllArgsConstructor

给类增加无参构造器、指定参数的构造器、包含所有参数的构造器

  • @NoArgsConstructor:使用在类上,提供一个无参构造器。当类中有final字段没有被初始化时,编译器会报错,此时可用@NoArgsConstructor(force = true),然后就会为没有初始化的final字段设置默认值 0 / false / null, 这样编译器就不会报错。对于具有约束的字段(例如@NonNull字段),不会生成检查或分配,因此请注意,正确初始化这些字段之前,这些约束无效
  • @RequiredArgsConstructor:使用在类上,生成构造方法(可能带参数也可能不带参数),若带参数,只能是类中所有带有 @NonNull 注解的和以final修饰的未经初始化的字段
  • @AllArgsConstructor:使用在类上,该注解提供一个全参数的构造方法,默认不提供无参构造。 这里的全参不包括已初始化的final字段哦~
  • @RequiredArgsConstructor(staticName = “of”)会生成一个of()的静态方法,并把构造方法设置为私有的
  • 这三个注解都会忽略static变量,另外会有一@java.beans.ConstructorProperties注解的构造函数,且至少有一个参数,它的作用是提供修改构造函数的一个桥接
package com.demo.lombok;

import lombok.AllArgsConstructor;

@AllArgsConstructor(staticName="allargs")
public class Tester{
    private String m_first;
}

反编译后

package com.demo.lombok;

public class Tester
{
  private String m_first;

  public static Tester allargs(String m_first)
  {
    return new Tester(m_first);
  }

  private Tester(String first)
  {
    this.m_first = first;
  }
}
package com.demo.lombok;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

@NoArgsConstructor(force = true)
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> {
    private int x, y;
    final int z=3;
    final int unInit;
    @NonNull
    private T description;

    public static void main(String[] args) {
        ConstructorExample<String> constructorExampleNoArgs = new ConstructorExample<String>();
        ConstructorExample<String> constructorExampleRequiredArgs = new ConstructorExample<String>(4, "desc");
        ConstructorExample<String> constructorExampleAllArgs = new ConstructorExample<String>(1, 2, 4, "desc");
    }
}

贴上反编译后的文件,结合上面的文字描述理解

package com.demo.lombok;

import lombok.NonNull;

public class ConstructorExample<T>
{
  private int x;
  private int y;

  public ConstructorExample()
  {
    this.unInit = 0;
  }

  private ConstructorExample(int unInit, @NonNull T description)
  {
    if (description == null) {
      throw new NullPointerException("description is marked @NonNull but is null");
    }
    this.unInit = unInit;this.description = description;
  }

  public static <T> ConstructorExample<T> of(int unInit, @NonNull T description)
  {
    return new ConstructorExample(unInit, description);
  }

  protected ConstructorExample(int x, int y, int unInit, @NonNull T description)
  {
    if (description == null) {
      throw new NullPointerException("description is marked @NonNull but is null");
    }
    this.x = x;this.y = y;this.unInit = unInit;this.description = description;
  }

  final int z = 3;
  final int unInit;
  @NonNull
  private T description;

  public static void main(String[] args)
  {
    ConstructorExample<String> constructorExampleNoArgs = new ConstructorExample();
    ConstructorExample<String> constructorExampleRequiredArgs = new ConstructorExample(4, "desc");
    ConstructorExample<String> constructorExampleAllArgs = new ConstructorExample(1, 2, 4, "desc");
  }
}

lombok.anyConstructor.suppressConstructorProperties 如果设置成true,lombok不生成@java.beans.ConstructorProperties注解的构造函数

lombok.anyConstructor.suppressConstructorProperties = [true | false] (default: false)

【注意】经过试验,默认不配置的时候并没有生成@ConstructorProperties标注的构造函数,只有当在lombok.config中显式配置为false的时候才生成,暂时没有找到原因(找到原因后修正mark一下)

对比下面的demo和本小节的第一段代码反编译的结果(类是相同的类)

修改lombok.config配置文件,添加

clear lombok.anyConstructor.suppressConstructorProperties
lombok.anyConstructor.suppressConstructorProperties = false
package com.demo.lombok;

import lombok.AllArgsConstructor;

@AllArgsConstructor(staticName="allargs")
public class Tester{
    private String m_first;
}
package com.demo.lombok;

import java.beans.ConstructorProperties;

public class Tester
{
  private String m_first;

  public static Tester allargs(String m_first)
  {
    return new Tester(m_first);
  }

  @ConstructorProperties({"first"})
  private Tester(String first)
  {
    this.m_first = first;
  }
}

可以结合lombok.config配置文件进行喜好设置:禁用、使用警告、允许(默认not set即允许)

lombok.anyConstructor.flagUsage = [warning | error] (default: not set) 
lombok.[allArgsConstructor|requiredArgsConstructor|noArgsConstructor].flagUsage = [warning | error] (default: not set)

@Data

  • @Data 包含了 @ToString、@EqualsAndHashCode、@Getter / @Setter和@RequiredArgsConstructor的功能,如果显式地声明构造函数将不会生成新的构造函数。而且@Data不能设置callSuper, includeFieldNames 和exclude等,如果需要改变默认值,可以使用相应的注解
  • getter和setter默认是public的,如果需要改变修饰符请使用@Setter和@Getter
  • transient 类型的变量不会被hashcode和equals使用,静态变量也会被跳过
  • 虽然@Data注解非常有用,但是它没有与其他注解相同的控制粒度。@Data提供了一个可以生成静态工厂的单一参数,将staticConstructor参数设置为所需要的名称,Lombok自动生成的构造函数设置为私有,并提供公开的给定名称的静态工厂方法@Data(staticConstructor=”of”)
  • 同时使用@Data 和 @AllArgsConstructor 后 ,默认的无参构造函数失效,如果需要它,要重新设置 @NoArgsConstructor
package com.demo.lombok;

import lombok.AccessLevel;
import lombok.Data;
import lombok.Setter;
import lombok.ToString;

@Data
public class DataExample {
    private final String name;
    @Setter(AccessLevel.PACKAGE)
    private int age;
    private double score;
    private String[] tags;

    @ToString(includeFieldNames = true)
    @Data(staticConstructor = "of")
    public static class Exercise<T> {
        private final String name;
        private final T value;
    }
}

可以结合lombok.config配置文件进行喜好设置:禁用、使用警告、允许(默认not set即允许)

lombok.data.flagUsage = [warning | error] (default: not set) 

Value

  • 该注解用于修饰类,是@Data的不可变形式,字段都被修饰为private和final,默认的情况下不会生成settter
  • 默认类本身也是final的
  • 实际上@Value等价于final @ToString @EqualsAndHashCode @AllArgsConstructor @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) @Getter
package com.demo.lombok;

import lombok.AccessLevel;
import lombok.ToString;
import lombok.Value;
import lombok.experimental.NonFinal;
import lombok.experimental.Wither;

@Value
public class ValueExample {
    String name;
    @Wither(AccessLevel.PACKAGE)
    @NonFinal
    int age;
    double score;
    protected String[] tags;

    @ToString(includeFieldNames = true)
    @Value(staticConstructor = "of")
    public static class Exercise<T> {
        String name;
        T value;
    }
}

反编译后的文件:

package com.demo.lombok;

import java.util.Arrays;

public final class ValueExample
{
  private final String name;
  private int age;
  private final double score;
  protected final String[] tags;

  public ValueExample(String name, int age, double score, String[] tags)
  {
    this.name = name;this.age = age;this.score = score;this.tags = tags;
  }

  public String toString()
  {
    return "ValueExample(name=" + this.name + ", age=" + this.age + ", score=" + this.score + ", tags=" + Arrays.deepToString(this.tags) + ")";
  }

  public int hashCode()
  {
    int PRIME = 59;int result = 1;Object $name = this.name;result = result * 59 + ($name == null ? 43 : $name.hashCode());result = result * 59 + this.age;long $score = Double.doubleToLongBits(this.score);result = result * 59 + (int)($score ^ $score >>> 32);result = result * 59 + Arrays.deepHashCode(this.tags);return result;
  }

  public boolean equals(Object o)
  {
    if (o == this) {
      return true;
    }
    if (!(o instanceof ValueExample)) {
      return false;
    }
    ValueExample other = (ValueExample)o;Object this$name = this.name;Object other$name = other.name;
    if (this$name == null ? other$name != null : !this$name.equals(other$name)) {
      return false;
    }
    if (this.age != other.age) {
      return false;
    }
    if (Double.compare(this.score, other.score) != 0) {
      return false;
    }
    return Arrays.deepEquals(this.tags, other.tags);
  }

  public static final class Exercise<T>
  {
    private final String name;
    private final T value;

    public String toString()
    {
      return "ValueExample.Exercise(name=" + this.name + ", value=" + this.value + ")";
    }

    public static <T> Exercise<T> of(String name, T value)
    {
      return new Exercise(name, value);
    }

    private Exercise(String name, T value)
    {
      this.name = name;this.value = value;
    }

    public int hashCode()
    {
      int PRIME = 59;int result = 1;Object $name = this.name;result = result * 59 + ($name == null ? 43 : $name.hashCode());Object $value = this.value;result = result * 59 + ($value == null ? 43 : $value.hashCode());return result;
    }

    public boolean equals(Object o)
    {
      if (o == this) {
        return true;
      }
      if (!(o instanceof Exercise)) {
        return false;
      }
      Exercise<?> other = (Exercise)o;Object this$name = this.name;Object other$name = other.name;
      if (this$name == null ? other$name != null : !this$name.equals(other$name)) {
        return false;
      }
      Object this$value = this.value;Object other$value = other.value;return this$value == null ? other$value == null : this$value.equals(other$value);
    }
  }
}

可以结合lombok.config配置文件进行喜好设置:禁用、使用警告、允许(默认not set即允许)

lombok.value.flagUsage = [warning | error] (default: not set) 

@Builder

@Builder注释为你的类生成复杂的构建器API,它把我们的Bean类包装为一个构建者模式,编译时增加了一个Builder内部类和全字段的构造器。
使用方式:Xxx.builder().name(“Adam Savage”).city(“San Francisco”).job(“Mythbusters”).job(“Unchained Reaction”).build();
@Builder可以放置在类上,构造方法上和普通方法上
在方法上使用@Builder,会生成以下事件

  • 生成一个名为XxxBuilder(Xxx为类名)的内部静态类,与静态方法builder的返回类型相同
  • 在builder方法中,针对目标的每个参数都是一个私有非静态非final的
  • 在bulider方法中,有一个无参的私有构造函数
  • 在bulider方法中,每个参数都有一个类似setter的方法,且返回类型是XxxBuilder
  • 在bulider方法中,一个build()方法,返回类型是目标类型Xxx
  • 在bulider方法中,具有一个toString()方法
  • 目标方法中调用builder()产生新的builder

如果其中有字段或参数没有初始化,它们一直是0、null、false。如果@Builder放在类上(而不是放在方法和构造方法上)可以在字段上直接使用@Builder.Default指定默认值

如果使用注解@Singular注解一个参数(@Builder注解方法或构造方法)或者一个字段(@Builder注解类),lombok将作为集合处理builder,生成两个adder方法而不是一个setter

package com.demo.lombok;

import java.util.Set;

import lombok.Builder;
import lombok.Singular;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

@Builder
@ToString
@Slf4j
public class BuilderExample {
    @Builder.Default
    private long created = System.currentTimeMillis();
    private String name;
    private int age;
    @Singular
    private Set<String> occupations;

    public static void main(String[] args) {
        BuilderExample builderExample = BuilderExample.builder().age(1).name("test").build();
        mylog.info("builderExample.toString() : {}",builderExample.toString());
    }
}

反编译的老长了~ 读者自己反编译一下不贴了

@SneakyThrows

该注解的作用是将检查异常包装为运行时异常,那么编码时就无需处理异常了。自动抛受检异常, 而无需显式在方法上使用throws语句。把checked异常转化为unchecked异常,好处是不用再往上层方法抛出了

提示:这并不是友好的编码方式,因为你编写的api的使用者,不能显式的获知需要处理检查异常

package com.demo.lombok;

import java.io.UnsupportedEncodingException;

import lombok.SneakyThrows;

public class SneakyThrowsExample implements Runnable {
    @SneakyThrows(UnsupportedEncodingException.class)
    public String utf8ToString(byte[] bytes) {
        return new String(bytes, "UTF-8");
    }

    @SneakyThrows
    public void run() {
        throw new Throwable();
    }
}

可以结合lombok.config配置文件进行喜好设置:禁用、使用警告、允许(默认not set即允许)
lombok.sneakyThrows.flagUsage = [warning | error] (default: not set)

@Synchronized

这个注解用在类方法(static修饰)或者实例方法上,效果和synchronized关键字相同,区别在于锁对象不同,对于类方法和实例方法,synchronized关键字的锁对象分别是类的class对象和this对象,而@Synchronized得锁对象分别是私有静态final对象$LOCK和私有final对象$lock(如果字段不存在,则会创建),当然,也可以自己指定锁对象

给方法加上同步锁,如果直接指定了value=xxx,其中xxx为类的一个成员,那么该方法使用该成员xxx作为加锁对象,放在同步块中执行

package com.demo.lombok;

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

这里写图片描述

package com.demo.lombok;

import java.io.PrintStream;

public class SynchronizedExample
{
  private final Object readLock = new Object();
  private static final Object $LOCK = new Object[0];

  public static void hello()
  {
    synchronized ($LOCK)
    {
      System.out.println("world");
    }
  }

  private final Object $lock = new Object[0];

  /* Error */
  public int answerToLife()
  {
    ……
  }

  public void foo()
  {
    synchronized (this.readLock)
    {
      System.out.println("bar");
    }
  }
}

可以结合lombok.config配置文件进行喜好设置:禁用、使用警告、允许(默认not set即允许)
lombok.synchronized.flagUsage = [warning | error] (default: not set)

@Getter(lazy=true)

@Getter(lazy = true)
标注字段为懒加载字段,懒加载字段在创建对象时不会进行真正的初始化,而是在第一次访问的时候才会初始化,后面再次访问也不会重复初始化

如果Bean的一个字段的初始化是代价比较高的操作,比如加载大量的数据;同时这个字段并不是必定使用的。那么使用懒加载机制,可以保证节省资源。

package com.demo.lombok;

import lombok.Getter;

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

可以结合lombok.config配置文件进行喜好设置:禁用、使用警告、允许(默认not set即允许)

lombok.getter.lazy.flagUsage = [warning | error] (default: not set)

@Log

这个注解用在类上,可以省去从日志工厂生成日志对象这一步,直接进行日志记录,具体注解根据日志工具的不同而不同,同时,可以在注解中使用topic来指定生成log对象时的类名。不同的日志注解总结如下(上面是注解,下面是实际作用)
生成log对象,用于记录日志,可以通过topic属性来设置getLogger(String name)方法的参数 例如 @Log4j(topic = “com.xxx.entity.User”),默认是类的全限定名,即 类名.class,log支持以下几种:

注解实际创建对象
@CommonsLogCreates private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@FloggerCreates private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();
@JBossLogCreates private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
@LogCreates private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
@Log4jCreates private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
@Log4j2Creates private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
@Slf4jCreates private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
@XSlf4jCreates private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

设置logger名字,默认是log

lombok.log.fieldName = an identifier (default: log).

默认情况下生成的logger是static,设置成false则是一个实例字段

lombok.log.fieldIsStatic = [true | false] (default: true)

可以结合lombok.config配置文件进行喜好设置:禁用、使用警告、允许(默认not set即允许)

lombok.log.flagUsage = [warning | error] (default: not set)
lombok.log.apacheCommons.flagUsage = [warning | error] (default: not set)
lombok.log.flogger.flagUsage = [warning | error] (default: not set)
lombok.log.jbosslog.flagUsage = [warning | error] (default: not set)
lombok.log.javaUtilLogging.flagUsage = [warning | error] (default: not set)
lombok.log.log4j.flagUsage = [warning | error] (default: not set)
lombok.log.log4j2.flagUsage = [warning | error] (default: not set)
lombok.log.slf4j.flagUsage = [warning | error] (default: not set)
lombok.log.xslf4j.flagUsage = [warning | error] (default: not set)

使用Lombok的注意事项

  • 项目中要使用lombok 不仅ide要支持(否则一堆错误),项目中也要引入jar包
  • 如果配置lombok.config文件,修改文件的属性值后,并不会自动重新编译class文件,ide编辑器也不会自动更新,所有每次修改配置文件后最后关闭java文件窗口重新打开,并且clean下项目

不足

  • 当与基于注释的对象关系映射(ORM)框架结合使用时,数据类的注释数量可能开始变得笨拙。这很大程度上被Lombok注释取代的代码量所抵消。
  • .java 文件无法再展示 .class 文件所包含的内容。
  • 特定 Lombok 转换将根本地改变 Java 语法。@SneakyThrows 转换就是个明显的例子。它允许不在方法定义中声明所检查的异常,而将其扔掉,如同它们是未经检查的异常,给排查问题带来一定的障碍
@SneakyThrows
public void doSomething() { 
  throw new Exception();
}

还有一些扩展的特性,得空再写 可以参考官网

lombok.config配置系统
lombok特性(一)
lombok特性(二)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值