一. 为什么要用@XXXArgsConstructor?
构造函数是一种特殊的方法。它主要用来与new运算符一起初始化对象,为对象成员变量赋初始值。因为构造函数也是一种方法,所以一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们,即构造函数的重载。当开发者没有为一个类编写构造函数时,Java会自动为该类生成一个无参的构造函数。如果开发者为一个了显示地提供了一个构造函数,那么Java便不会再自动生成一个无参的构造函数。
同样,开发者在类中去编写各种重载的构造函数会占用很大的篇幅。例如,我们为Student类提供一个全参的构造函数:
public class Student {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
}
可以看到,如果我们再去新增一个成员变量后,就要去改这个构造函数。Lombok提供了三种构造函数用来解决这类问题,它们分别是@ NoArgsConstructor,@ RequiredArgsConstructor,@ AllArgsConstructor。因为这三个注解都与构造函数相关,考虑到文章名长度问题,我统称为@XXXArgsConstructor注解。
二. @XXXArgsConstructor如何使用?
@NoArgsConstructor注解使用
@NoArgsConstructor将生成没有参数的构造函数。如果一个类仅仅只需要一个无参的构造函数,那么完全没有必要使用@NoArgsConstructor注解,因为Java会在编译时,为没有构造函数的类自动生成一个无参的构造函数。所以@NoArgsConstructor注解需要在已经存在构造函数时使用,才显得有意义。
@RequiredArgsConstructor注解使用
@RequiredArgsConstructor为每个需要特殊处理的字段生成一个带有1个参数的构造函数。所有未初始化的final字段以及标有@NonNull注解的字段。对于标有@NonNull注解的字段,还将生成一个显式的null检查。
虽然,在定义final字段的时候,可以不初始化该字段,但是这种做法很不好。因为final就是用来表明一个字段是常量,既然是常量,就应该初始化它。 此处理解不正确,我改正一下。在被final修饰的实例变量(也就是没有被static修饰),它是可以不初始化该实例变量。这种实例常量,是完全可以在初始化实例对象的时候,初始化该实例常量。当程序中新增一个学生时,该学生的性别是确定,而且一直不可变,所以性别可以作为实例常量。但是,如果一个类常量就必须显示地提供初始化值,在编译之前就要提供值,也就是说程序运行之前,该值就已经明确了。
final修饰的成员变量(类变量和实例变量)必须由程序员显示地指定初始值:
类变量:必须在静态初始化代码块中指定初始值或声明该类变量时指定初始值,而且只能在这两个地方的其中之一指定;
实例变量:必须在非静态初始化代码块,声明该实例变量时或构造器中指定初始值,而且只能在这三个地方的其中之一指定。
这里我们可以看到注解属性access = AccessLevel.PROTECTED,这是用来指定该构造函数的访问控制符。
@AllArgsConstructor注解使用
@AllArgsConstructor为类中的每个字段生成一个带有1个参数的构造函数,标记为的字段@NonNull将对这些参数进行空检查。这里我们使用注解属性staticName来生成静态工厂方法,使用of作为静态工厂方法名。(注:of的静态工厂方法在jdk中越来越流行)编译后,查看反编译文件,如图:
可以看到在of方法里,调用全参的构造函数,但是这个构造函数已经是私有的。同学肯定有疑问,既然是直接调用了全参的构造函数,那么这个staticName注解属性的作用在哪里?我们此注解并没有增加什么效果,仅仅是为了改变方法名吗?其实不然,此注解属性主要用于推断泛型。
我们将Student改为泛型类Student<T, K>,当不使用staticName时,new一个Student对象。
使用staticName后,用of获取一个对象。
或许你感觉没什么太大改进,其实,我也这么觉得。😄
官网上对于staticName优点的解释也只有这么多,就是创建的时候方便一些。
三. @XXXArgsConstructor源码
因为三个注解的注解属性很多相同,access和staticName的理解是相同的,onConstructor注解属性参考Lombok实验室之onX使用。
元注解都是:@Target(ElementType.TYPE),@Retention(RetentionPolicy.SOURCE)
@NoArgsConstructor源码
package lombok;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Generates a no-args constructor.
* Will generate an error message if such a constructor cannot be written due to the existence of final fields.
* <p>
* Complete documentation is found at <a href="https://projectlombok.org/features/Constructor">the project lombok features page for @Constructor</a>.
* <p>
* Even though it is not listed, this annotation also has the {@code onConstructor} parameter. See the full documentation for more details.
* <p>
* NB: Fields with constraints such as {@code @NonNull} will <em>NOT</em> be checked in a {@code @NoArgsConstructor} constructor, of course!
*
* @see RequiredArgsConstructor
* @see AllArgsConstructor
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface NoArgsConstructor {
/**
* If set, the generated constructor will be private, and an additional static 'constructor'
* is generated with the same argument list that wraps the real constructor.
*
* Such a static 'constructor' is primarily useful as it infers type arguments.
*
* @return Name of static 'constructor' method to generate (blank = generate a normal constructor).
*/
String staticName() default "";
/**
* Any annotations listed here are put on the generated constructor.
* The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br>
* up to JDK7:<br>
* {@code @NoArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br>
* from JDK8:<br>
* {@code @NoArgsConstructor(onConstructor_={@AnnotationsGohere})} // note the underscore after {@code onConstructor}.
*
* @return List of annotations to apply to the generated constructor.
*/
AnyAnnotation[] onConstructor() default {};
/**
* Sets the access level of the constructor. By default, generated constructors are {@code public}.
*
* @return The constructor will be generated with this access modifier.
*/
AccessLevel access() default lombok.AccessLevel.PUBLIC;
/**
* If {@code true}, initializes all final fields to 0 / null / false.
* Otherwise, a compile time error occurs.
*
* @return Return {@code} true to force generation of a no-args constructor, picking defaults if necessary to assign required fields.
*/
boolean force() default false;
/**
* Placeholder annotation to enable the placement of annotations on the generated code.
* @deprecated Don't use this annotation, ever - Read the documentation.
*/
@Deprecated
@Retention(RetentionPolicy.SOURCE)
@Target({})
@interface AnyAnnotation {}
}
force注解属性:当存在为初始化的final字段时,设置为true,则会final字段设置初始值 0/null/false 其中一种。如果,存在final字段,并且force的值为false,则会发生编译错误。
@RequiredArgsConstructor源码
package lombok;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Generates a constructor with required arguments.
* Required arguments are final fields and fields with constraints such as {@code @NonNull}.
* <p>
* Complete documentation is found at <a href="https://projectlombok.org/features/Constructor">the project lombok features page for @Constructor</a>.
* <p>
* Even though it is not listed, this annotation also has the {@code onConstructor} parameter. See the full documentation for more details.
*
* @see NoArgsConstructor
* @see AllArgsConstructor
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface RequiredArgsConstructor {
/**
* If set, the generated constructor will be private, and an additional static 'constructor'
* is generated with the same argument list that wraps the real constructor.
*
* Such a static 'constructor' is primarily useful as it infers type arguments.
*
* @return Name of static 'constructor' method to generate (blank = generate a normal constructor).
*/
String staticName() default "";
/**
* Any annotations listed here are put on the generated constructor.
* The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br>
* up to JDK7:<br>
* {@code @RequiredArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br>
* from JDK8:<br>
* {@code @RequiredArgsConstructor(onConstructor_={@AnnotationsGohere})} // note the underscore after {@code onConstructor}.
*
* @return List of annotations to apply to the generated constructor.
*/
AnyAnnotation[] onConstructor() default {};
/**
* Sets the access level of the constructor. By default, generated constructors are {@code public}.
*
* @return The constructor will be generated with this access modifier.
*/
AccessLevel access() default lombok.AccessLevel.PUBLIC;
/**
* Placeholder annotation to enable the placement of annotations on the generated code.
* @deprecated Don't use this annotation, ever - Read the documentation.
*/
@Deprecated
@Retention(RetentionPolicy.SOURCE)
@Target({})
@interface AnyAnnotation {}
}
@AllArgsConstructor源码
package lombok;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Generates an all-args constructor.
* An all-args constructor requires one argument for every field in the class.
* <p>
* Complete documentation is found at <a href="https://projectlombok.org/features/Constructor">the project lombok features page for @Constructor</a>.
* <p>
* Even though it is not listed, this annotation also has the {@code onConstructor} parameter. See the full documentation for more details.
*
* @see NoArgsConstructor
* @see RequiredArgsConstructor
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface AllArgsConstructor {
/**
* If set, the generated constructor will be private, and an additional static 'constructor'
* is generated with the same argument list that wraps the real constructor.
*
* Such a static 'constructor' is primarily useful as it infers type arguments.
*
* @return Name of static 'constructor' method to generate (blank = generate a normal constructor).
*/
String staticName() default "";
/**
* Any annotations listed here are put on the generated constructor.
* The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br>
* up to JDK7:<br>
* {@code @AllArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br>
* from JDK8:<br>
* {@code @AllArgsConstructor(onConstructor_={@AnnotationsGohere})} // note the underscore after {@code onConstructor}.
*
* @return List of annotations to apply to the generated constructor.
*/
AnyAnnotation[] onConstructor() default {};
/**
* Sets the access level of the constructor. By default, generated constructors are {@code public}.
*
* @return The constructor will be generated with this access modifier.
*/
AccessLevel access() default lombok.AccessLevel.PUBLIC;
/**
* Placeholder annotation to enable the placement of annotations on the generated code.
*
* @deprecated Don't use this annotation, ever - Read the documentation.
*/
@Deprecated
@Retention(RetentionPolicy.SOURCE)
@Target({})
@interface AnyAnnotation {}
}
四. 特别说明
本文已经收录在Lombok注解系列文章总览中,并继承上文中所提的特别说明。
源码地址:gitee