Lombok简介及使用详解

一、官方介绍

官网地址及介绍如下:

Lombok官网地址:https://projectlombok.org/

Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again. Early access to future java features such as val, and much more.

二、功能作用        

以往项目中,你一定遇到过在一个JavaBean中增加或者删除一个成员变量,就要重新生成相应的Getter、Setter等方法,非常地繁琐。现在,通过在项目中引入Lombok,就能解决这些问题。通过Lombok提供的注解,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法,来简化代码。且Lombok的好处绝不止于此,其中还有许多注释,允许对类的结构和行为进行更细粒度的控制。

本文以Maven项目为例,添加如下的maven依赖方式使用Lombok:

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>0.9.2</version>
    </dependency>
</dependencies>

三、常用注解详解

1、Getter和Setter

@Getter和@Setter注释分别为字段生成getter和setter。且@Getter和@Setter注释都采用可选参数来指定生成方法的访问级别。

注:如果带注释的字段所属的类包含与要生成的getter或setter同名的方法,无论参数或返回类型如何,都不会生成相应的方法。

示例代码:

@Getter @Setter private boolean employed = true;
// 默认为public权限,此处设定Setter方法为protected
@Setter(AccessLevel.PROTECTED) private String name;

等同于:

private boolean employed = true;
private String name;

public boolean isEmployed() {
    return employed;
}

public void setEmployed(final boolean employed) {
    this.employed = employed;
}

protected void setName(final String name) {
    this.name = name;
}

2、Nonull

@NonNull注释用于对对应的成员进行快速失败null检查。当放置在Lombok正在为其生成setter方法的字段上时,将生成null检查,如果提供了null值,将导致NullPointerException。此外,如果Lombok正在为所属类生成一个构造函数,那么该字段将被添加到构造函数签名中,null检查将包含在生成的构造函数代码中。

示例代码:

@Getter @Setter @NonNull
private List<Person> members;

等同于:

@NonNull
private List<Person> members;

public Family(@NonNull final List<Person> members) {
    if (members == null) throw new java.lang.NullPointerException("members");
    this.members = members;
}
    
@NonNull
public List<Person> getMembers() {
    return members;
}

public void setMembers(@NonNull final List<Person> members) {
    if (members == null) throw new java.lang.NullPointerException("members");
    this.members = members;
}

3、@ToString

此注释生成toString方法的实现。默认情况下,任何非静态字段都将以名称-值对的形式包含在方法的输出中。如果需要,可以通过将注释参数includeFieldNames设置为false来抑制输出中属性名的包含。通过在exclude参数中包含它们的字段名,可以将特定字段从生成的方法的输出中排除。或者,参数的作用是只列出输出中需要的字段。

示例代码:

@ToString(callSuper=true,exclude="someExcludedField")
public class Foo extends Bar {
    private boolean someBoolean = true;
    private String someStringField;
    private float someExcludedField;
}

4、@EqualsAndHashCode

这个类级别的注释将使Lombok生成equals和hashCode方法,因为这两个方法通过hashCode契约内在地联系在一起。默认情况下,这两个方法都将考虑类中任何非静态或瞬态的字段。与@ToString类似,提供了exclude参数,以防止字段包含在生成的逻辑中。还可以使用参数的形式只列出应该考虑的字段。与@ToString一样,这个注释也有一个callSuper参数。在考虑当前类中的字段之前,通过从超类中调用equals来验证等式。对于hashCode方法,结果是将超类的hashCode结果合并到散列的计算中。在将callSuper设置为true时,要小心确保父类中的equals方法正确地处理实例类型检查。如果父类检查这个类是否是特定类型的,而不仅仅是两个对象的类是相同的,这可能会导致不期望的结果。如果超类使用的是Lombok生成的equals方法,这不是问题。但是,其他实现可能不能正确处理这种情况。还要注意,当类只扩展对象时,不能将callSuper设置为true,因为这会导致实例相等检查,从而缩短字段的比较。这是由于生成的方法调用Object上的equals实现,如果比较的两个实例不是同一个实例,则返回false。因此,在这种情况下,Lombok将生成编译时错误。

示例代码:

@EqualsAndHashCode(callSuper=true,exclude={"address","city","state","zip"})
public class Person extends SentientBeing {
    enum Gender { Male, Female }

    @NonNull private String name;
    @NonNull private Gender gender;
    
    private String ssn;
    private String address;
    private String city;
    private String state;
    private String zip;
}

等同于:

public class Person extends SentientBeing {
    
    enum Gender {
        /*public static final*/ Male /* = new Gender() */,
        /*public static final*/ Female /* = new Gender() */;
    }
    @NonNull
    private String name;
    @NonNull
    private Gender gender;
    private String ssn;
    private String address;
    private String city;
    private String state;
    private String zip;
    
    @java.lang.Override
    public boolean equals(final java.lang.Object o) {
        if (o == this) return true;
        if (o == null) return false;
        if (o.getClass() != this.getClass()) return false;
        if (!super.equals(o)) return false;
        final Person other = (Person)o;
        if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;
        if (this.gender == null ? other.gender != null : !this.gender.equals(other.gender)) return false;
        if (this.ssn == null ? other.ssn != null : !this.ssn.equals(other.ssn)) return false;
        return true;
    }
    
    @java.lang.Override
    public int hashCode() {
        final int PRIME = 31;
        int result = 1;
        result = result * PRIME + super.hashCode();
        result = result * PRIME + (this.name == null ? 0 : this.name.hashCode());
        result = result * PRIME + (this.gender == null ? 0 : this.gender.hashCode());
        result = result * PRIME + (this.ssn == null ? 0 : this.ssn.hashCode());
        return result;
    }
}

4、@Data

@Data注释可能是Lombok工具集中最常用的注释。它结合了@ToString、@EqualsAndHashCode、@Getter和@Setter的功能。从本质上讲,在类上使用@Data与使用默认的@ToString和@EqualsAndHashCode注释类以及使用@Getter和@Setter注释每个字段是一样的。带有@Data的注释类也会触发Lombok构造函数生成。这将添加一个公共构造函数,它接受任何@NonNull或final字段作为参数。这提供了一个普通旧Java对象(POJO)所需的一切。虽然@Data非常有用,但它没有提供与其他Lombok注释相同的控制粒度。为了覆盖默认的方法生成行为,可以使用另一个Lombok注释注释类、字段或方法,并指定必要的参数值以达到预期效果。@Data确实提供了一个参数选项,可用于生成静态工厂方法。将staticConstructor参数的值设置为所需的方法名称,将导致Lombok将生成的构造函数设置为私有,并公开给定名称的静态工厂方法。

示例代码:

@Data(staticConstructor="of")
public class Company {
    private final Person founder;
    private String name;
    private List<Person> employees;
}

等同于:

public class Company {
    private final Person founder;
    private String name;
    private List<Person> employees;
    
    private Company(final Person founder) {
        this.founder = founder;
    }
    
    public static Company of(final Person founder) {
        return new Company(founder);
    }
    
    public Person getFounder() {
        return founder;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(final String name) {
        this.name = name;
    }
    
    public List<Person> getEmployees() {
        return employees;
    }
    
    public void setEmployees(final List<Person> employees) {
        this.employees = employees;
    }
    
    @java.lang.Override
    public boolean equals(final java.lang.Object o) {
        if (o == this) return true;
        if (o == null) return false;
        if (o.getClass() != this.getClass()) return false;
        final Company other = (Company)o;
        if (this.founder == null ? other.founder != null : !this.founder.equals(other.founder)) return false;
        if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;
        if (this.employees == null ? other.employees != null : !this.employees.equals(other.employees)) return false;
        return true;
    }
    
    @java.lang.Override
    public int hashCode() {
        final int PRIME = 31;
        int result = 1;
        result = result * PRIME + (this.founder == null ? 0 : this.founder.hashCode());
        result = result * PRIME + (this.name == null ? 0 : this.name.hashCode());
        result = result * PRIME + (this.employees == null ? 0 : this.employees.hashCode());
        return result;
    }
    
    @java.lang.Override
    public java.lang.String toString() {
        return "Company(founder=" + founder + ", name=" + name + ", employees=" + employees + ")";
    }
}

5、@Cleanup

可以使用@Cleanup注释来确保释放所分配的资源。当用@Cleanup注释本地变量时,任何后续代码都被包装在try/finally块中,以确保清除方法在当前作用域的末尾被调用。默认情况下,@Cleanup假设这种清除方法与输入和输出流一样被命名为“关闭”。但是,可以为注释的值参数提供不同的方法名称。只有不带参数的清理方法才能与此注释一起使用。在使用@Cleanup注释时,还需要注意一点。如果清除方法抛出异常,它将抢占方法体中抛出的任何异常。这可能导致问题被隐藏的实际原因,在选择使用Project Lombok的资源管理时应该考虑到这一点。此外,由于Java 7即将实现自动资源管理,这种注释可能会相对较短。

示例代码:

public void testCleanUp() {
    try {
        @Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(new byte[] {'Y','e','s'});
        System.out.println(baos.toString());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

等同于:

public void testCleanUp() {
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            baos.write(new byte[]{'Y', 'e', 's'});
            System.out.println(baos.toString());
        } finally {
            baos.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

6、@Synchronized

在一个方法上使用synchronized关键字可能会导致不好的结果,任何一个开发多线程软件的开发人员都可以证明这一点。synchronized关键字在实例方法或静态方法的类对象中会锁定当前对象。这意味着开发人员控制之外的代码有可能锁定同一个对象,从而导致死锁。通常建议在单独的对象上显式锁定,该对象专门用于此目的,而不是以允许非请求锁定的方式公开。项目Lombok为此提供了@Synchronized注释。使用@Synchronized注释实例方法将提示Lombok生成一个名为$lock的私有锁定字段,该方法将在执行之前锁定该字段。类似地,以同样的方式注释静态方法将生成一个名为$LOCK的私有静态对象,用于以相同的方式使用静态方法。可以通过向注释的值参数提供字段名来指定不同的锁定对象。提供字段名时,开发人员必须定义该属性,因为Lombok不会生成该属性。

示例代码:

private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");

@Synchronized
public String synchronizedFormat(Date date) {
    return format.format(date);
}

等同于:

private final java.lang.Object $lock = new java.lang.Object[0];
private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");

public String synchronizedFormat(Date date) {
    synchronized ($lock) {
        return format.format(date);
    }
}

7、@SneakyThrows

默认情况下,@ sneakythrow将允许抛出任何检查过的异常,而不需要在throw子句中声明。通过提供可抛出类的数组(Class<?将Throwable>扩展到注释的值参数。

示例代码:

@SneakyThrows
public void testSneakyThrows() {
    throw new IllegalAccessException();
}

等同于:

public void testSneakyThrows() {
    try {
        throw new IllegalAccessException();
    } catch (java.lang.Throwable $ex) {
        throw lombok.Lombok.sneakyThrow($ex);
    }
}

8、@Log

这个注解用在类上,可以省去从日志工厂生成日志对象这一步,直接进行日志记录,具体注解根据日志工具的不同而不同,同时,可以在注解中使用topic来指定生成log对象时的类名。不同的日志注解总结如下(上面是注解,下面是实际作用):

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

猜你喜欢

转载自blog.csdn.net/qushaming/article/details/84233075