资讯专栏INFORMATION COLUMN

lombok

张迁 / 298人阅读

摘要:不可用于类级别。是否使用父类的和方法,注意父类为时无法设置为。注解源代码,另外两个注解的源码和这个很类似,就不贴出来了。过时的生成包含必须处理的字段的构造器,如修饰必须初始化注解修饰的。

相识

lombok想必已经有很多人已经使用了很长时间了,而我却是第一次接触到,有点呆。lombok主要是用于减少重复代码,通过一组简单的注释取代一些重复的Java代码。对于lombok的评价褒贬不一,有的人觉得特别方便,有的人觉得改变了一成不变的代码结构,增加了代码维护成本(有的人没有用过lombok),我是觉得每一个工具诞生肯定是有他诞生的价值的,多学一个是一个啊,小老弟,用不用再说。:)

官方文档地址

官方API地址

官方注解介绍地址

准备 1、下载idea插件

我这里已经安装好了,没有安装的时候按钮应该是Install

2、pom文件引入project lombok的maven依赖

    org.projectlombok
    lombok
    1.18.2
    provided

lombok maven版本

注解介绍 @Getter和@Setter

顾名思义,生成get和set方法的注解,@Getter和@Setter发生在编译阶段,编译之后,@Getter和@Setter相关的注解就消失了,取而代之的是相应的get和set方法。

public class LombokTest {
    @Getter @Setter
    private boolean flag;
}

lombok遵循了boolean类型的get方法的约定,我们来看一下编译后的代码(等效Java代码)吧。

public class LombokTest {
    private boolean flag;

    public LombokTest() {
    }

    public boolean isFlag() {
        return this.flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

可以看到boolean类型的get方法是isFlag()而不是getFlag()。我们还可定义生成get和set方法的访问级别。

public class LombokTest {
    @Getter(AccessLevel.PUBLIC)
    @Setter(AccessLevel.PROTECTED)
    private String name;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;

    public LombokTest() {
    }

    public String getName() {
        return this.name;
    }

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

可以看到setName的访问级别是protected。lombok提供了下面几种级别。

AccessLevel枚举类的源代码。

public enum AccessLevel {
    PUBLIC, // public
    MODULE, // 编译后相当于 default
    PROTECTED, // protected
    PACKAGE, // default
    PRIVATE, // private
    NONE; // 不生成

    private AccessLevel() {
    }
}

最后@Getter和@Setter注解是可以写在类级别的,作用于所有的成员变量,无法细粒度的控制访问级别。

@Getter(AccessLevel.MODULE)
@Setter(AccessLevel.PROTECTED)
public class LombokTest {
    private String name;
    private boolean flag;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private boolean flag;

    public LombokTest() {
    }

    String getName() {
        return this.name;
    }

    boolean isFlag() {
        return this.flag;
    }

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

    protected void setFlag(boolean flag) {
        this.flag = flag;
    }
}
@NonNull

变量空检查,如果@NonNull注解使用在构造函数的字段时,构造函数也会进行空变量检查。不可用于类级别。

public class LombokTest {
    @NonNull
    @Setter
    @Getter
    private String name;
    
    public LombokTest(@NonNull String name) {
        this.name = name;
    }
}

编译后的代码(等效Java代码)

public class LombokTest {
    @NonNull
    private String name;

    public LombokTest(@NonNull String name) {
        if (name == null) {
            throw new NullPointerException("name is marked @NonNull but is null");
        } else {
            this.name = name;
        }
    }

    public void setName(@NonNull String name) {
        if (name == null) {
            throw new NullPointerException("name is marked @NonNull but is null");
        } else {
            this.name = name;
        }
    }

    @NonNull
    public String getName() {
        return this.name;
    }
}
@ToString

用于生成toString()方法。只能用于类级别。static修饰的变量是不能生成在toString()方法中的,原因是static修饰的变量在类创建的时候就生成了,只有一个;而普通的变量属于对象,每创建一个新的对象都会有一个。可以理解为static修饰的变量属于类,而普通变量数据对象,这样可能好理解一点。

@ToString
public class LombokTest {
    private String name;
    private static String phone;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private static String phone;

    public LombokTest() {
    }

    public String toString() {
        return "YmlProperties(name=" + this.name + ")";
    }
}

默认情况下出了static修饰的字段都将以name-value的形式生成在toString()方法中。同时toString注解中还可以设置一些属性来细粒度的控制toString()方法。

下面是ToString注解的源码和解释:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface ToString {
    boolean includeFieldNames() default true; // 生成toString()方法时默认为 name-value的形式,设置为false时 则 value的形式。

    String[] exclude() default {}; // 不包含指定字段

    String[] of() default {}; // 包含指定字段

    boolean callSuper() default false; // 是否调用父类的toString()方法

    boolean doNotUseGetters() default false; // 生成toString()方法是否直接访问字段,设置为true时直接访问字段(如:this.name),false时通过getter()方法进行访问(如:this.getName())。当然如果没有生成getter()方法,无论是否设置都直接访问字段

    boolean onlyExplicitlyIncluded() default false; // 是否仅仅包含添加了@ToString.Include的字段,默认为false。

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Exclude { // 使用@ToString.Exclude修饰字段,需要和@ToString注解一起使用,多带带使用无效。
    }

    @Target({ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Include {// 使用@ToString.Include修饰字段,需要和@ToString注解一起使用,多带带使用无效。
        int rank() default 0;

        String name() default "";
    }
}

注意:

of和exclude属性设置多个字段时,以字符串数组的形式,如:

@ToString(of = {"name","phone"}) // 包含name和phone字段
@ToString(exclude = {"name","phone"}) // 不包含name和phone字段

使用@ToString.Include或者@ToString.Exclude注解时也需要使用@ToString注解,否者无效。

@ToString
public class LombokTest {
    @ToString.Include
    private String name;
    @ToString.Exclude
    private String phone;
}
@EqualsAndHashCode

生成equals()和hashCode()方法。默认使用所有的非static和非transient字段生成这两个方法。

@EqualsAndHashCode
public class LombokTest {
    private String name;
    private String phone;
    private boolean flag;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private String phone;
    private boolean flag;

    public LombokTest() {
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof LombokTest)) {
            return false;
        } else {
            LombokTest other = (LombokTest)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                label39: {
                    Object this$name = this.name;
                    Object other$name = other.name;
                    if (this$name == null) {
                        if (other$name == null) {
                            break label39;
                        }
                    } else if (this$name.equals(other$name)) {
                        break label39;
                    }

                    return false;
                }

                Object this$phone = this.phone;
                Object other$phone = other.phone;
                if (this$phone == null) {
                    if (other$phone != null) {
                        return false;
                    }
                } else if (!this$phone.equals(other$phone)) {
                    return false;
                }

                if (this.flag != other.flag) {
                    return false;
                } else {
                    return true;
                }
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof LombokTest;
    }

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

和ToString注解相似也可以通过设置一些属性来控制决堤生成的hashCode()和equals()方法。下面是@EqualsAndHashCode注解的源码和解释,和@ToString一样的属性就没有写注释了。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface EqualsAndHashCode {
    String[] exclude() default {};

    String[] of() default {};

    boolean callSuper() default false;// 是否使用父类的equals和toString方法,注意:父类为Object时无法设置为true。

    boolean doNotUseGetters() default false;

    EqualsAndHashCode.AnyAnnotation[] onParam() default {};

    boolean onlyExplicitlyIncluded() default false;

    /** @deprecated */
    @Deprecated
    @Retention(RetentionPolicy.SOURCE)
    @Target({})
    public @interface AnyAnnotation { // 过时的方法(deprecated)
    }

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Exclude {
    }

    @Target({ElementType.FIELD, ElementType.METHOD})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Include {
        String replaces() default "";
    }
}
@Data

这是lombok中使用的最多的注解,相当于@ToString@EqualsAndHashCode@RequiredArgsConstructor@Getter@Setter五个注解的功能。虽然使用起来方法,但是失去了细粒度的控制。如果需要细粒度控制,则需要进行注解的覆盖。

@Data
public class LombokTest {
    private String name;
    private String phone;
    private boolean flag;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private String phone;
    private boolean flag;

    public LombokTest() {
    }

    public String getName() {
        return this.name;
    }

    public String getPhone() {
        return this.phone;
    }

    public boolean isFlag() {
        return this.flag;
    }

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

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof LombokTest)) {
            return false;
        } else {
            LombokTest other = (LombokTest)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                label39: {
                    Object this$name = this.getName();
                    Object other$name = other.getName();
                    if (this$name == null) {
                        if (other$name == null) {
                            break label39;
                        }
                    } else if (this$name.equals(other$name)) {
                        break label39;
                    }

                    return false;
                }

                Object this$phone = this.getPhone();
                Object other$phone = other.getPhone();
                if (this$phone == null) {
                    if (other$phone != null) {
                        return false;
                    }
                } else if (!this$phone.equals(other$phone)) {
                    return false;
                }

                if (this.isFlag() != other.isFlag()) {
                    return false;
                } else {
                    return true;
                }
            }
        }
    }

    protected boolean canEqual(Object other) {
        return other instanceof LombokTest;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $name = this.getName();
        int result = result * 59 + ($name == null ? 43 : $name.hashCode());
        Object $phone = this.getPhone();
        result = result * 59 + ($phone == null ? 43 : $phone.hashCode());
        result = result * 59 + (this.isFlag() ? 79 : 97);
        return result;
    }

    public String toString() {
        return "LombokTest(name=" + this.getName() + ", phone=" + this.getPhone() + ", flag=" + this.isFlag() + ")";
    }
}

下面看一下@Data注解的源码。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Data {
    String staticConstructor() default "";
}

可以看到提供了一个,staticConstructor(),这个方法的作用是私有化构造器,并生成一个静态的获取实例的方法,这样就可通过类名来获取实例(如:LombokTest.getLombokTest())

@Data(staticConstructor = "getLombokTest")
private LombokTest() {
}

public static LombokTest getLombokTest() {
    return new LombokTest();
}
// ...省略其他代码
@Cleanup

自动释放资源,主要用于IO流的操作,不需要手动编写释放资源的try/finally代码块。

public class LombokTest {
    public static void main(String[] args) throws Exception {
        @Cleanup InputStream is = new FileInputStream(args[0]);
        @Cleanup OutputStream os = new FileOutputStream(args[0]);
    }
}

编译后的代码(等效Java代码)

public class LombokTest {
    public static void main(String[] args) throws Exception {
        FileInputStream is = new FileInputStream(args[0]);

        try {
            OutputStream os = new FileOutputStream(args[0]);
            if (Collections.singletonList(os).get(0) != null) {
                os.close();
            }
        } finally {
            if (Collections.singletonList(is).get(0) != null) {
                is.close();
            }
        }
    }
}

还是挺方便的。再来看看@Cleanup注解的源代码

@Target({ElementType.LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface Cleanup {
    String value() default "close";
}

可以看到默认调用的是close方法,也就是说我们可以设置finally方法体具体调用的流处理方法。如:

public static void main(String[] args) throws Exception {
    @Cleanup(value = "available") InputStream is = new FileInputStream(args[0]);
    @Cleanup OutputStream os = new FileOutputStream(args[0]);
}

编译后的代码(等效Java代码)

具体可以指定哪些字段,需要流对象支持哪些方法了。如InputStream:

@Synchronized

同步代码块,只能使用在类方法(static)或者对象方法(普通方法)上,synchronized关键字锁住的是this,@Synchronized注解默认使用的锁为$lock,静态的锁为$LOCK,也可以自己指定锁对象,如示例中的:world

public class LombokTest {
    @Synchronized
    public void hello() {

    }

    @Synchronized
    public static void helloWorld() {

    }

    private final Object world = new Object();
    
    @Synchronized("world")
    public void world() {

    }
}

编译后的代码(等效Java代码)

public class LombokTest {
    private final Object $lock = new Object[0];
    private static final Object $LOCK = new Object[0];
    private final Object world = new Object();

    public LombokTest() {
    }

    public void hello() {
        Object var1 = this.$lock;
        synchronized(this.$lock) {
            ;
        }
    }

    public static void helloWorld() {
        Object var0 = $LOCK;
        synchronized($LOCK) {
            ;
        }
    }

    public void world() {
        Object var1 = this.world;
        synchronized(this.world) {
            ;
        }
    }
}
@SneakyThrows

个人觉得没有啥意义,唯一的作用可能就是不用手动捕获异常或者向上抛出,而是通过lombok帮你捕获。。。可以指定捕获的异常类型,默认捕获的是Throwable。

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

    @SneakyThrows(IllegalAccessException.class)
    public void testSneakyThrows2() {
        throw new IllegalAccessException();
    }
}

编译后的代码(等效Java代码)

public class LombokTest {
    public LombokTest() {
    }

    public void testSneakyThrows() {
        try {
            throw new IllegalAccessException();
        } catch (Throwable var2) {
            throw var2;
        }
    }

    public void testSneakyThrows2() {
        try {
            throw new IllegalAccessException();
        } catch (IllegalAccessException var2) {
            throw var2;
        }
    }
}

感觉有点呆。。。官方建议在没有深思熟虑之前不要使用这个注解。。。下面是官网上的一句话:

You can pass any number of exceptions to the @SneakyThrows annotation. If you pass no exceptions, you may throw any exception sneakily.

@NoArgsConstructor,@ RequiredArgsConstructor,@ AllArgsConstructor

@NoArgsConstructor 生成一个无参构造器。

@NoArgsConstructor注解源代码,另外两个注解的源码和这个很类似,就不贴出来了。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface NoArgsConstructor {
    String staticName() default ""; // 如果指定了相应字段,则私有化构造并生成一个静态的获取实例方法。

    NoArgsConstructor.AnyAnnotation[] onConstructor() default {};

    AccessLevel access() default AccessLevel.PUBLIC;

    boolean force() default false;

    /** @deprecated */
    @Deprecated
    @Retention(RetentionPolicy.SOURCE)
    @Target({})
    public @interface AnyAnnotation { // 过时的(deprecated)
    }
}

@ RequiredArgsConstructor 生成包含必须处理的字段的构造器,如:final修饰(必须初始化)、@NonNull注解修饰的。

@RequiredArgsConstructor
public class LombokTest {
    private String name;
    private String phone;
    private transient boolean flag;
    private final String finalFiled;
    private static int num;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    private String phone;
    private transient boolean flag;
    private final String finalFiled;
    private static int num;

    public LombokTest(String finalFiled) {
        this.finalFiled = finalFiled;
    }
}

@ AllArgsConstructor 生成一个全参构造器,除static修饰的字段。@NonNull可以组合使用。

@AllArgsConstructor
public class LombokTest {
    private String name;
    @NonNull
    private String phone;
    private transient boolean flag;
    private final String finalFiled;
    private static int num;
}

编译后的代码(等效Java代码)

public class LombokTest {
    private String name;
    @NonNull
    private String phone;
    private transient boolean flag;
    private final String finalFiled;
    private static int num;

    public LombokTest(String name, @NonNull String phone, boolean flag, String finalFiled) {
        if (phone == null) {
            throw new NullPointerException("phone is marked @NonNull but is null");
        } else {
            this.name = name;
            this.phone = phone;
            this.flag = flag;
            this.finalFiled = finalFiled;
        }
    }
}
小结

学到这里常用的Lombok注解基本上都有了,如果想要根据深入的学习,建议去官网看。官网地址文章开始已经列出来了,加油!!!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75306.html

相关文章

  • lombok的使用

    摘要:虽然有人可能会说里面都自带自动生成这些方法的功能,但是使用会使你的代码看起来更加简洁,写起来也更加方便。使用不使用自动生成方法使用不使用自动生成无参数构造函数。 一、lombok简介 lombok是在学习过程中发现的一个非常好用的小工具,用了之后感觉的确很不错,所以特此来推荐一下。 lombok的官方地址:https://projectlombok.org/ lombok的Github...

    MobService 评论0 收藏0
  • 使用神器Lombok优雅编码

    摘要:提高编码效率使代码更简洁消除冗长代码避免修改字段名字时忘记修改方法名提高下逼格以上就是的优点,当然,的优点远远不止以上几点,使用,你可以更加优雅高效的编辑代码。实战完成了上述准备之后,就可以愉快的使用进行编码了。接下来是使用简化后的代码。 Lombok介绍 近来偶遇一款撸码神器,介绍给大家~相信许多小伙伴都深有体会,POJO类中的千篇一律的getter/setter,construct...

    _ang 评论0 收藏0
  • 途牛原创|使用 lombok 简化 Java 代码

    摘要:使用,简化代码为了简化与,提供了一种机制,帮助我们自动生成这些样板代码。但是,在实际项目中,完全没有使用到。源码审查是一个源码审查工具。最新版已经支持的全部注解,不再认为是没有使用的变量。 一个典型的 Java 类 public class A { private int a; private String b; public int getA() { ret...

    RyanHoo 评论0 收藏0
  • Lombok使用

    摘要:为方法或构造函数的参数生成检查语句,相当于生成的检查语句会插入到方法的最前端,如果是构造函数,则在或调用之后插入检查。 Lombok简介 Lombok是一个可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法。 ...

    张金宝 评论0 收藏0
  • 使用Lombok来精简你的项目代码

    摘要:什么是一言以蔽之是一种工具,提供了简单的注解来简化我们的重复冗长代码。但在实际的项目中,我们常常只在定义的时候用上,而在业务代码中很少用到。总结是个非常有用的工具,能够帮助我们精简很多臃肿冗长的代码,不过也有其局限性,推荐在定义中使用。 什么是Lombok 一言以蔽之:lombok是一种工具,提供了简单的注解来简化我们的重复冗长Java代码。比如一个Java Bean,注解了lombo...

    AJie 评论0 收藏0
  • Lombok pojo类小神器

    摘要:可以去下载包目前最新版本为。对于某个具体的类来说,出于安全或者性能或者其它方面的考虑,可能并不希望全部成员都出现在方法的返回值里。根据中的建议,方法和方法要同时实现,并且保证一致性。 前言 Lombok主页 Lombok下载 Lombok引入项目之后,便可以使用 本文记录了在项目中应用Lombok时的使用案例,希望对朋友你有一些帮助。 可以去 下载jar包 maven: ...

    stackfing 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<