@lombok从源码层面学习

文章目录

    • 概述
    • 环境
    • 注解
    • @Getter 、@Setter
      • @Getter
      • @Setter
    • @NonNull
    • @ToString


概述

lombok我们主要用来减少get、set和空参和全参的重复性代码,本文希望深入学习来理解其原理和避免一些坑。其官网描述1为:

Project Lombok aims to reduce the prevalence of some of the worst offenders by replacing them with a simple set of annotations.
(Project Lombok 旨在通过用一组简单的注解替换一些最严重的违例来减少它们的流行。)

关键字:lombok学习,@Getter ,@Setter,@NonNull,@ToString,@EqualsAndHashCode,@Data,@Cleanup,@Synchronized,@SneakyThrows

环境

  • jdk:1.8
  • projectlombok:1.18.24

注解

  • @Getter 、@Setter
  • @NonNull
  • @ToString
  • @EqualsAndHashCode
  • @Data
  • @Cleanup
  • @Synchronized
  • @SneakyThrows

@Getter 、@Setter

@Getter

@Getter注解用来生成成员变量的get方法。

@Getter private String catName

等效于java代码:

private String catName;
public String getCatName() {
	return this.catName;
    }

其中@Getter包含三个可选参数value(default lombok.AccessLevel.PUBLIC)、onMethod(default {})、lazy(default false)

  1. value是用来指定生成的get方法的访问级别,默认为public,可以使用提供的访问级别枚举进行指定

    public enum AccessLevel {
    	PUBLIC, MODULE, PROTECTED, PACKAGE, PRIVATE,
    	/** Represents not generating anything or the complete lack of a method. */
    	NONE;
    }
    

    比如指定为private

    @Getter(value = AccessLevel.PRIVATE) private String catName;
    

    等效于java代码

    private String catName;
    private String getCatName() {
        return this.catName;
    }
    
  2. onMethod的类型是AnyAnnotation[ ],在生成的getter方法上加上特定的注解列表(例子中的@Interface仅代表是注解,并无实际含义),注意onMethod后面的下划线。

    @Getter(onMethod_ = @Interface)
    private String catName;
    

    等效于java代码

    private String catName;
    @Interface
    public String getCatName() {
        return this.catName;  
    }
    
  3. lazy这个属性默认为false,类似于单例模式的懒加载,当实例化该类比较耗时,且占用较大的空间时,我们不希望频繁的创建新的实例,而使用lazy进行修饰字段时,这个字段相对于其它字段部分会隐藏起来,使用一个原子缓存,并且不需要进行并发锁的处理,因为lombok会进行锁的操作,在get的时候会进行双重检验来创建对象,注意lazy修饰的字段必须是final类型,所以需要进行初始化。

    @Getter(lazy = true)
    private final String catName = "Tom";
    

    等效于java代码

    private final AtomicReference<Object> catName = new AtomicReference();
        public String getCatName() {
            Object value = this.catName.get();
            if (value == null) {
                synchronized(this.catName) {
                    value = this.catName.get();
                    if (value == null) {
                        String actualValue = "Tom";
                        value = "Tom" == null ? this.catName : "Tom";
                        this.catName.set(value);
                    }
                }
            }
            return (String)((String)(value == this.catName ? null : value));
        }
    

@Setter

@Setter注解是用来生成成员变量的set方法

@Setter private String dogName;

等效于java代码

private String dogName;
public void setDogName(final String dogName) {
    this.dogName = dogName;
}

其中@Setter包含三个可选参数value(default lombok.AccessLevel.PUBLIC)、onMethod(default {})、onParam(default {}),而@Setter中的参数value,onMethod和@Gette的用法和意义一致,这里不再赘述。

onParam和onMethod的用法和意义都很类似,区别在于一个作用于整个方法上,而onParam可以单独给成员变量设置不同的注解列表。他们的源码对比如下:


	/**
	 * Any annotations listed here are put on the generated method.
	 * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).
* up to JDK7:
* {@code @Setter(onMethod=@__({@AnnotationsGoHere}))}
* from JDK8:
* {@code @Setter(onMethod_={@AnnotationsGohere})} // note the underscore after {@code onMethod}. * * @return List of annotations to apply to the generated setter method. */
AnyAnnotation[] onMethod() default {}; /** * Any annotations listed here are put on the generated method's parameter. * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).
* up to JDK7:
* {@code @Setter(onParam=@__({@AnnotationsGoHere}))}
* from JDK8:
* {@code @Setter(onParam_={@AnnotationsGohere})} // note the underscore after {@code onParam}. * * @return List of annotations to apply to the generated parameter in the setter method. */
AnyAnnotation[] onParam() default {};

@NonNull

lombok的@NonNull注解是用来检查是否为空的,注意不要和其它的@NonNull注解弄混,其源码描述为:

If put on a parameter, lombok will insert a null-check at the start of the method
/ constructor’s body, throwing a{@code NullPointerException} with the
parameter’s name as message. If put on a field, any generated method
assigning a value to this field will also produce these null-checks.

如果将@NonNull放置在一个参数上,lombok将在方法/构造函数体的开头插入一个null-check,如果为空则抛出一个带参数名的NullPointerException。如果放在一个字段上,任何为该字段赋值的生成方法也会产生这些空检查。

源码的解释翻译一下就是,加上@NonNull注解的字段,任何赋值操作都会检查空值,如果为空就抛出NullPointerException。

@Getter
@Setter
public class Mouse {
    @NonNull
    private String name;
}

等效于java代码

public class Mouse {
    @NonNull
    private String name;

    public Mouse() {
    }

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

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

@ToString

@ToString注解是用来生成toString方法,打印有关的字段值,其源码描述为:

Generates an implementation for the {@code toString} method inherited
by all objects, consisting of printing the values of relevant fields.
为所有对象继承的{@code toString}方法生成一个实现,包括打印相关字段的值。

根据官方文档描述:

Annotating a class with @ToString will cause lombok to generate an
implementation of the toString() method. You use configuration options
to specify whether field names should be included but otherwise the
format is fixed: the class name followed by parentheses containing
fields separated by commas, e.g. MyClass(foo=123, bar=234).
使用注释类@ToString将导致 lombok生成该toString()方法的实现。您可以使用配置选项来指定是否应包含字段名称,否则格式是固定的:类名称后跟括号,其中包含以逗号分隔的字段,例如MyClass(foo=123,bar=234).

当只使用@ToString注解,参数均为默认值时:

@ToString
public class Dog {
    private String name;

    private Integer age;

    private Double weight;
}

等效于java代码:

public class Dog {
    private String name;
    private Integer age;
    private Double weight;

    public Dog() {
    }

    public String toString() {
        return "Dog(name=" + this.name + ", age=" + this.age + ", weight=" + this.weight + ")";
    }
}

@ToString包含的参数有

  1. includeFieldNames(default true)

Include the name of each field when printing it.
打印时包含每个字段的名称。默认开启

includeFieldNames为false时,则不会打印字段的名称,只打印值。如:

@ToString(includeFieldNames = false)
public class Dog {
    private String name;

    private Integer age;

    private Double weight;
}

等效于java代码:

public class Dog {
    private String name;
    private Integer age;
    private Double weight;

    public Dog() {
    }

    public String toString() {
        return "Dog(" + this.name + ", " + this.age + ", " + this.weight + ")";
    }
}
  1. exclude(default {})

Any fields listed here will not be printed in the generated toString
implementation. Mutually exclusive with of().
这里列出的任何字段都不会在生成的toString实现中打印,与of()互斥。

当exclude参数包含字段名时,不会打印包含的字段值。如:

@ToString(exclude = "name")
public class Dog {
    private String name;

    private Integer age;

    private Double weight;
}

等效于java代码:

public class Dog {
    private String name;
    private Integer age;
    private Double weight;

    public Dog() {
    }

    public String toString() {
        return "Dog(age=" + this.age + ", weight=" + this.weight + ")";
    }
}
  1. of(default {})

If present, explicitly lists the fields that are to be printed.
Normally, all non-static fields are printed. Mutually exclusive with
exclude(). 如果存在,则显式列出要打印的字段。通常,所有非静态字段都被打印。 与exclude()相互排斥。

这里的of与exclude刚好相反,需要打印出来的显示的写出来。如:

@ToString(of = {"age","weight"})
public class Dog {
    private String name;

    private Integer age;

    private Double weight;
}

等效于java代码:

public class Dog {
    private String name;
    private Integer age;
    private Double weight;

    public Dog() {
    }

    public String toString() {
        return "Dog(age=" + this.age + ", weight=" + this.weight + ")";
    }
}
  1. callSuper(default false)

Include the result of the superclass’s implementation of toString in
the output. 在输出中包含父类实现toString的结果。默认false。

如果设为true,则会一起输出父类的toString方法。如:

@ToString(callSuper = true)
public class Dog extends Animal{
    private String name;

    private Integer age;

    private Double weight;
}

等效于java代码:

public class Dog extends Animal {
    private String name;
    private Integer age;
    private Double weight;

    public Dog() {
    }

    public String toString() {
        return "Dog(super=" + super.toString() + ", name=" + this.name + ", age=" + this.age + ", weight=" + this.weight + ")";
    }
}
  1. doNotUseGetters(default false)

Normally, if getters are available, those are called. To suppress this
and let the generated code use the fields directly, set this to true.
通常,如果getter可用,就会调用它们。要抑制这种情况并让生成的代码直接使用字段,请将此设置为true。

当设置为true时,toString时字段的get方法将会失效,会直接使用字段的值。如:

@Getter
@ToString(doNotUseGetters = false)
public class Dog extends Animal{
    private String name;

    private Integer age;

    private Double weight;
}

//等效于java代码:
public class Dog extends Animal {
    private String name;
    private Integer age;
    private Double weight;

    public Dog() {
    }

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

    public Integer getAge() {
        return this.age;
    }

    public Double getWeight() {
        return this.weight;
    }

    public String toString() {
        return "Dog(name=" + this.getName() + ", age=" + this.getAge() + ", weight=" + this.getWeight() + ")";
    }
}

当设置为true时,toString时get方法失效:

@Getter
@ToString(doNotUseGetters = true)
public class Dog extends Animal{
    private String name;

    private Integer age;

    private Double weight;
}
//等效于java代码:
public class Dog extends Animal {
    private String name;
    private Integer age;
    private Double weight;

    public Dog() {
    }

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

    public Integer getAge() {
        return this.age;
    }

    public Double getWeight() {
        return this.weight;
    }

    public String toString() {
        return "Dog(name=" + this.name + ", age=" + this.age + ", weight=" + this.weight + ")";
    }
}
  1. onlyExplicitlyIncluded(default false)

Only include fields and methods explicitly marked with
@ToString.Include. Normally, all (non-static) fields are included by
default. 只包含显式标记为@ToString.Include的字段和方法。通常,默认情况下包括所有(非静态)字段。

当设置为true时,只包含显示要toString的字段:

@ToString(onlyExplicitlyIncluded = true)
public class Dog extends Animal{
    @ToString.Include
    private String name;

    private Integer age;

    private Double weight;
}

等效于java代码:

public class Dog extends Animal {
    private String name;
    private Integer age;
    private Double weight;

    public Dog() {
    }

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

包含的注解有

  1. @Exclude{}
    作用和exclude参数的作用一样,使用注解的方式可以更加灵活的放置在字段上,排除掉不需要toString的字段。
@ToString
public class Dog extends Animal{
    @ToString.Exclude
    private String name;

    private Integer age;

    private Double weight;
}

等效于java代码:

public class Dog extends Animal {
    private String name;
    private Integer age;
    private Double weight;

    public Dog() {
    }

    public String toString() {
        return "Dog(age=" + this.age + ", weight=" + this.weight + ")";
    }
}
  1. @Include{int rank() default 0;String name() default “”;}
    它有两个参数rank和name。
    ● rank(default 0),设置优先级,数字大的先打印,如果设置的数字相同,则按顺序打印,即先排优先级,再按顺序
    ● name(default “”),在字段上设置别名,打印时将会输出这个别名
@ToString
public class Dog extends Animal{
    @ToString.Include(rank = 1,name = "test")
    private String name;
    @ToString.Include(rank = 2)
    private Integer age;
    @ToString.Include(rank = 2)
    private Double weight;
}

等效于java代码:

public class Dog extends Animal {
    private String name;
    private Integer age;
    private Double weight;

    public Dog() {
    }

    public String toString() {
        return "Dog(age=" + this.age + ", weight=" + this.weight + ", test=" + this.name + ")";
    }
}

------------------To Be Continued


  1. Michael Kimberlin. REDUCING BOILERPLATE CODE WITH PROJECT LOMBOK[EB/OL]. 2010.1[2022.5.13]. https://objectcomputing.com/resources/publications/sett/january-2010-reducing-boilerplate-code-with-project-lombok. ↩︎

你可能感兴趣的:(注解,学习,java,开发语言)