您还在为POJO繁琐的Getter/Setter方法,构造方法的编写所烦恼吗?现在,伟大的Lombok插件为我们广大的“搬砖工人”们排忧解难。本文使用版本为1.18.10在使用本插件之前请先给IDE安装Lombok插件。

Lombok是一个java库,它自动插入到编辑器和构建工具中,增强java的性能。不要再编写另一个Getter或equals方法,使用一个注释,您的类有一个功能齐全的生成器,自动记录变量,等等。

@Getter

      @Getter注解可以作用在类上或者具体的字段上,如果作用在类上则所有的非静态属性都会生成Getter方法,否则只给具体作用的字段添加Getter方法。

//编译前
public class GetterTest {
    @Getter
    private String name;
}

//编译后
public class GetterTest {
    private String name;
    public GetterTest() {}
    public String getName() {
        return this.name;
    }
}

AccessLevel访问级别

      默认生成的的Getter方法访问级别是public的,我们可以通过设置Getter的value控制生成的Getter的访问级别。Lombok提供的访问级别如下:

public enum AccessLevel {
    PUBLIC, MODULE, PROTECTED, PACKAGE, PRIVATE,
    /** 不生成任何Getter方法*/
    NONE;
}

onMethod额外注解

      @Getter注解提供onMethod属性,它可为我们生成的Getter方法添加额外的注解,配合校验框架(如hibernate-validator)味道才是最佳。不过在JDK7(及以下)和JDK8(及以上)上的使用方法上有些不同。

JDK7:@Getter(onMethod=@__({@AnnotationsGoHere}))
JDK8:@Getter(onMethod_={@AnnotationsGohere})//注意onMethod后的下划线
//编译前
public class GetterTest {
    @Getter
    private String name;
    //可添加多个注解
    @Getter(
            onMethod_ = {@NotNull,@Max(10)}
    )
    private Integer age;
}
//编译后
public class GetterTest {
    private String name;
    private Integer age;
    public GetterTest() {
    
    public String getName() {
        return this.name;
    }

    @NotNull
    @Max(10L)
    Integer getAge() {
        return this.age;
    }
}

懒加载

      @Getter通过lazy属性控制是否进行懒加载,默认不懒加载。Lombok通过使用双重校验锁方式实现。

//编译前
public class GetterTest {
    @Getter(lazy = true)
    private final String CONSTANT = "hcp";
}
//编译后
public class GetterTest {
    private final AtomicReference<Object> CONSTANT = new AtomicReference();
    public GetterTest() {
    }
    public String getCONSTANT() {
        Object value = this.CONSTANT.get();
        if (value == null) {
            synchronized(this.CONSTANT) {
                value = this.CONSTANT.get();
                if (value == null) {
                    String actualValue = "hcp";
                    value = "hcp" == null ? this.CONSTANT : "hcp";
                    this.CONSTANT.set(value);
                }
            }
        }
        return (String)((String)(value == this.CONSTANT ? null : value));
    }
}

@Setter

      @Setter注解与@Getter注解类似,不同的地方在于@Setter注解不存在lazy属性,并且不仅可以在方法上添加注解(设置onMethod)还可以在参数上设置注解(设置onParam)。

up to JDK7:@Setter(onParam=@__({@AnnotationsGoHere}))
from JDK8:@Setter(onParam_={@AnnotationsGohere})//注意onParam后的下划线
//编译前
public class SetterTest {
    @Setter(onParam_ = {@NotNull})
    private String name;
}
//编译后

public class SetterTest {
    private String name;
    public SetterTest() {}

    public void setName(@NotNull String name) {
        this.name = name;
    }
}

@ToString

      @ToString默认会为我们生成toString方法。只支持标注在类上。

//编译前
@ToString
public class ToStringTest {
    private String name;
    private Integer age;
    private String address;
}
//编译后
public class ToStringTest {
    private String name;
    private Integer age;
    private String address;
    public ToStringTest() {}
    public String toString() {
        return "ToStringTest(name=" + this.name + ", age=" + this.age + ", address=" + this.address + ")";
    }
}

includeFieldNames

      默认生成的toString方法格式为:类名(字段名1=?...),我们可以通过设置includeFieldNames=false将字段名排除掉。

@ToString(includeFieldNames = false)
...
public String toString() {
    return "ToStringTest(" + this.name + ", " + this.age + ", " + this.address + ")";
}

exclude排除字段

      默认会将所有的字段都打印出来,我们可以通过设置exclude将某些不需要的字段排除掉,不生成在toString方法中。注:此属性将会被备注为过时,将使用@ToString.Exclude注解代替

@ToString(exclude = {"name","age"})
...
public String toString() {
    return "ToStringTest(address=" + this.address + ")";
}

//未来使用方法
ToString
public ToStringTest {
    @ToString.Exclude//作用在字段上
    private String name;
    private Integer age;
    private String address;
}

of指定字段

      与exclude相反,设置of指定某些字段生成在toString方法中。如果有相同的字段都设置在了excludeof中,of的优先级更高,以此为准。注:此属性将会被备注为过时,将使用@ToString.Include与ToString(onlyExplicitlyIncluded = true)一起完成

@ToString(exclude = {"name","age"},of = {"age","address"})
...
public String toString() {
    return "ToStringTest(age=" + this.age + ", address=" + this.address + ")";
}

//未来使用方法
//onlyExplicitlyIncluded设为true表示只添加标注了Include的非静态字段
ToString(onlyExplicitlyIncluded = true)
public ToStringTest {
    //作用在字段上和方法上,如果作用在方法上则会将方法的返回值添加到toString方法中
    //可以通过设置name指定字段名,通过设置rank指定字段的先后顺序
    @ToString.Include
    private String name;
    private Integer age;
    private String address;

    @ToString.Include
    private Date now(){
        return new Date();
    }
}
//编译后
public String toString() {
    return "ToStringTest(name=" + this.name + ", now=" + this.now() + ")";
}

      注意:新旧两种方法不能同时存在。

doNotUseGetters

      如果你的类中存在getter方法,toString方法中将使用想相应的getter方法获取字段值,如果您需要直接获取字段值需要将doNotUseGetters设置为true。

//默认
@ToString
public class ToStringTest {
    private String name;
    private Integer age;
    private String address;
    public String getName(){
        System.out.println("get name");
        return this.name;
    }
}
public String toString() {
    return "ToStringTest(name=" + this.getName() + ", age=" + this.age + ", address=" + this.address + ")";
}

//doNotUseGetters=true
public String toString() {
    return "ToStringTest(name=" + this.name + ", age=" + this.age + ", address=" + this.address + ")";
}

@EqualsAndHashCode

      @EqualsAndHashCode注解为类生成equalshashCode方法。其内置属性和上面的@ToString注解几乎一致。

@Data

      @Data注解包括了@Getter、@Setter、@ToString和@EqualsAndHashCode这些注解。通过设置staticConstructor可以生成一个静态方法从而返回实例对象。

@Data(staticConstructor = "of")
public class DataTest {
    private String name;
    private Integer age;
}
//生成静态方法
public static DataTest of() {
    return new DataTest();
}

@NonNull

      @NonNull可以为我们做非空检查。

@Setter
public class NonNullTest {
    @NonNull
    private String name;
    private Integer age;
    public NonNullTest(@NonNull String name){
        System.out.println(name);
    }
}

//编译后
public class NonNullTest {
    @NonNull
    private String name;
    private Integer age;
    public NonNullTest(@NonNull String name) {
        if (name == null) {
            throw new NullPointerException("name is marked non-null but is null");
        } else {
            System.out.println(name);
        }
    }
    public void setName(@NonNull String name) {
        if (name == null) {
            throw new NullPointerException("name is marked non-null but is null");
        } else {
            this.name = name;
        }
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

Constructor相关注解

      Constructor相关注解包括以下三个注解:@AllArgsConstructor、@NoArgsConstructor、@RequiredArgsConstructor。前两个注解一个是自动生成全参构造方法,一个生成无参构造方法,第三个将自动判断类中的字段是否是必须的从而决定是否加入构造方法中。

@RequiredArgsConstructor
public class ConstructorTest {
    private final String name;
    @NonNull
    private Integer age;
    private String address;
}

//编译后
//由于name使用final修饰,它不能为空,age使用@NonNull标注也不能为空,所以这两个为required
public ConstructorTest(String name, @NonNull Integer age) {
    if (age == null) {
        throw new NullPointerException("age is marked non-null but is null");
    } else {
        this.name = name;
        this.age = age;
    }
}

@Cleanup

      @Cleanup注解能生成资源关闭相关代码。

public class CleanupTest {
    public void copyFile(String in,String out) throws Exception {
        //@Cleanup 注解仅能标注在本地变量中
        @Cleanup FileInputStream inputStream = new FileInputStream(in);
        @Cleanup FileOutputStream outputStream = new FileOutputStream(out);
        int r;
        while ((r = inputStream.read())!=-1){
            outputStream.write(r);
        }
    }
}
//编译后
public void copyFile(String in, String out) throws Exception {
    FileInputStream inputStream = new FileInputStream(in);
    try {
        FileOutputStream outputStream = new FileOutputStream(out);
        int r;
        try {
            while((r = inputStream.read()) != -1) {
                outputStream.write(r);
            }
        } finally {
            if (Collections.singletonList(outputStream).get(0) != null) {
                outputStream.close();
            }
        }
    } finally {
        if (Collections.singletonList(inputStream).get(0) != null) {
            inputStream.close();
        }
    }
}

Lombok优缺点

优点:

  • 通过注解自动生成模板代码,提高开发效率
  • 代码简洁
  • 新增(修改)属性后,无需修改相关方法

缺点:

  • 降低了源代码的可读性和完整性
  • 加大了问题排查难度
  • 需要IDE相关插件支持
Last modification:January 23rd, 2020 at 05:19 pm
如果觉得我的文章对你有用,请随意赞赏