我是靠谱客的博主 淡然啤酒,最近开发中收集的这篇文章主要介绍lombok特性(二)使用Lombok的注意事项不足,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

续 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特性(二)

最后

以上就是淡然啤酒为你收集整理的lombok特性(二)使用Lombok的注意事项不足的全部内容,希望文章能够帮你解决lombok特性(二)使用Lombok的注意事项不足所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部