一. 为什么要用@Value?
在开发过程中,不可变类其实经常用到。不可变类是指创建该类的实例后,该实例的实例变量是不可改变的。Jav提供的8个包装类和java.lang.String类都是不可变类。对于不可变类的理解可以先参考这篇博客《java中的不可变类》。现在,我们知道不可变类和其它普通类是不同的,它有以下几点具体要求:(注:不可变类的详细策略参考官网)
- 使用private和final修饰符来修饰该类的成员变量;
- 提供带参数的构造器,根据传入的参数来初始化类里的成员变量;
- 仅为该类的成员变量提供getter方法,不要提供setter方法,因为普通方法无法修改final修饰的成员变量;
- 不允许子类覆盖方法。最简单的方法是将该类声明为final。一种更复杂的方法是使用private工厂方法来构造构造器和构造实例。
- 如果有必要,重写Object类的hascode()和equals()方法。equals方法根据关键成员变量来作为两个对象是否相等的标准,除此之外,还应该保证两个用equals方法判断为相等的对象的hashCode()也相等。
在开发过程中,我们编写不可变类时需要满足以上条件,以Student类为例。
public final class Student {
private final String name;
private final Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public Integer getAge() {
return this.age;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Student)) {
return false;
} else {
Student other = (Student)o;
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
Object this$age = this.getAge();
Object other$age = other.getAge();
if (this$age == null) {
if (other$age != null) {
return false;
}
} else if (!this$age.equals(other$age)) {
return false;
}
return true;
}
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $name = this.getName();
int result = result * 59 + ($name == null ? 43 : $name.hashCode());
Object $age = this.getAge();
result = result * 59 + ($age == null ? 43 : $age.hashCode());
return result;
}
public String toString() {
return "Student(name=" + this.getName() + ", age=" + this.getAge() + ")";
}
可以看到,上面Student类就是一个典型的不可变类。这样的不可变类,用代码实现起来并不复杂,只是篇幅过长,而且不可变类的要求是相同的。所以,Lombok给出了一个注解@Value来表明一个类是不可变类。
二. @Value如何使用?
@Value的使用十分简单,只需在类上加上该注解即可。我们将上面的Student使用@Value改造一下:
@Value
public class Student {
String name;
Integer age;
}
就一个注释?对,就一个注释。编译后,查看反编译文件和上面是相同的。(上面的代码就是反编译出来的,我可没那么多时间自己实现不可变类:D)
三. @Value源码
package lombok;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Generates a lot of code which fits with a class that is a representation of an immutable entity.
* <p>
* Equivalent to {@code @Getter @FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) @AllArgsConstructor @ToString @EqualsAndHashCode}.
* <p>
* Complete documentation is found at <a href="https://projectlombok.org/features/Value">the project lombok features page for @Value</a>.
*
* @see lombok.Getter
* @see lombok.experimental.FieldDefaults
* @see lombok.AllArgsConstructor
* @see lombok.ToString
* @see lombok.EqualsAndHashCode
* @see lombok.Data
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface Value {
/**
* If you specify a static constructor name, then the generated constructor will be private, and
* instead a static factory method is created that other classes can use to create instances.
* We suggest the name: "of", like so:
*
* <pre>
* public @Value(staticConstructor = "of") class Point { final int x, y; }
* </pre>
*
* Default: No static constructor, instead the normal constructor is public.
*
* @return Name of static 'constructor' method to generate (blank = generate a normal constructor).
*/
String staticConstructor() default "";
}
只有一个staticConstructor注解属性,其用法和《Lombok之@Data使用》中的staticConstructor相同。
四. 特别说明
本文已经收录在Lombok注解系列文章总览中,并继承上文中所提的特别说明。
源码地址:gitee