反射是做几乎任何事情的最糟糕和最慢的方法。
你想要的是封装您的字段:将它们设为私有,以便您的班级完全控制它们的更改方式。
如果它们不是太多,您可以将它们全部设为 final 并让构造函数设置它们:
public class Person {
private final String firstName;
private final String lastName;
public Person(String firstName,
String lastName) {
this.firstName = Objects.requireNonNull(firstName,
"First name cannot be null.");
this.lastName = Objects.requireNonNull(lastName,
"Last name cannot be null.");
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
使用这种方法,外部代码完全不可能将字段设置为空值。¹如果调用者传递空值,则构造函数永远不会完成,因此永远不会存在具有空值字段的 Person 的现有实例。这意味着你可以制作一个书面保证调用者将永远不必检查值是否为空:
/**
* Returns this user's first name. This value will never be null.
*/
public String getFirstName() {
return firstName;
}
/**
* Returns this user's last name. This value will never be null.
*/
public String getLastName() {
return lastName;
}
您甚至可以更进一步,验证值,而不仅仅是检查 null:
public Person(String firstName,
String lastName) {
this.firstName = Objects.requireNonNull(firstName,
"First name cannot be null.");
this.lastName = Objects.requireNonNull(lastName,
"Last name cannot be null.");
if (firstName.isBlank()) {
throw new IllegalArgumentException("First name cannot be blank.");
}
if (lastName.isBlank()) {
throw new IllegalArgumentException("Last name cannot be blank.");
}
}
如果你有很多字段,你可以只使用 get-methods 和 set-methods 而不是在构造函数中设置所有内容。在这种情况下,将每个字段初始化为非空值是很有用的,因此该类能够提供与构造函数方法相同的非空保证:
public class Person {
private String firstName = "(unknown)";
private String lastName = "(unknown)";
private String socialSecurityNumber = "000-00-0000";
private LocalDate dateOfBirth = LocalDate.MAX;
public String getFirstName() {
return firstName;
}
public String setFirstName(String name) {
this.firstName = Objects.requireNonNull(name,
"Name cannot be null.");
}
public String getLastName() {
return lastName;
}
public String setLastName(String name) {
this.lastName = Objects.requireNonNull(name,
"Name cannot be null.");
}
public String getSocialSecurityNumber() {
return socialSecurityNumber;
}
public void setSocialSecuityNumber(String num) {
Objects.requireNonNull(num, "Argument cannot be null.");
if (!num.matches("\\d{3}-\\d{2}-\\d{4}")) {
throw new IllegalArgumentException(
"Argument must be in the format nnn-nn-nnnn.");
}
this.socialSecurityNumber = num;
}
public LocalDate getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(LocalDate date) {
Objects.requireNonNull(date, "Date cannot be null.");
int age = date.getYear() - LocalDate.now().getYear();
if (age > 150) {
throw new IllegalArgumentException(
"Invalid date: no one is more than 150 years old.");
}
if (age < 0) {
throw new IllegalArgumentException(
"Invalid date: cannot be born in the future.");
}
this.dateOfBirth = date;
}
}
1. 从技术上讲,外部代码可以使用反射的setAccessible 方法来破解不在模块中的类的私有成员,除非安装了 SecurityManager。然而,以这种方式侵入事物的人应该期望类以意想不到的方式破坏,因为他们本质上是“使保修失效”。意思是,这很糟糕,没有人应该这样做,事实上 Java 的更高版本不会让他们这样做。