我在网上发现了这个练习,其中我有两个类,我应该使 Tutor
类不可变。但是,我唯一能想到的就是将 final
添加到 name
字段。当谈到构造函数时,我认为我不需要更改 name
变量的初始化,因为 String
是不可变的。我不确定如何处理集合以及如何使构造函数的这一部分不可变。 根据练习,我不应该更改 Student 类(我可以看到它是可变的)
public class Student {
private String name;
private String course;
public Student(String name, String course) {
this.name = name;
this.course = course;
}
public String getName() {
return name;
}
public String getCourse() {
return course;
}
public void setName(String name) {
this.name = name;
}
public void setCourse(String course) {
this.course = course;
}
}
public final class Tutor {
private String name;
private final Set<Student> tutees;
public Tutor(String name, Student[] students) {
this.name = name;
tutees = new HashSet<Student>();
for (int i = 0; i < students.length; i++)
tutees.add(students[i]);
}
public Set<Student> getTutees() {
return Collections.unmodifiableSet(tutees);
}
public String getName() {
return name;
}
}
最佳答案
Tutor
类呈现出许多促进其不变性的方面:
- 类(class)已结束
Set<Student>
受到保护以防止修改- 没有方法允许直接更改类的状态
但是,构造函数的防御性副本并不完整。
它还必须复制 Student
s 传递的数组元素。否则,构造函数的客户端可能会更改它们的任何实例,并使 Tutor
实例可变,例如:
Student[] students = ...;
Tutor tutor = new Tutor(name, students);
students[0].setName("new Name!"); // break the immutability of Tutor
你应该写这样的内容:
public Tutor(String name, Student[] students){
this.name = name;
tutees = new HashSet<Student>();
for (Student student : students){
Student copy = new Student(student.getName(),
student.getCourse());
tutees.add(copy);
}
}
另外请注意 Set
返回者 getTutees()
不可修改,但其中包含的元素为 Student
是可变的。
因此,为了使 Tutor 不可变,您还必须在返回 getTutees()
时创建 Student 元素的副本。例如:
public Set<Student> getTutees(){
Set<Student> students = new HashSet<>();
for (Student student : tutees){
Student copy = new Student(student.getName(),
student.getCourse());
students.add(copy);
}
return Collections.unmodifiableSet(students);
}
正如您可能注意到的,在这些条件下获得不变性(我们希望不可变的实例,但包含引用可变实例的集合)需要编写更多代码(用于读取/维护/测试)并执行更多处理(执行速度较慢)。
如果Student
是一个不可变的类,原来的getTutees()
原来的构造函数就足够了。
关于Java,使类不可变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50257305/