Java,使类不可变

标签 java immutability

我在网上发现了这个练习,其中我有两个类,我应该使 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/

相关文章:

c# - 什么时候对不可变类型使用值和引用类型? (。网)

java - Download 类是不可变性的糟糕候选者吗?

c# - 您期望不可变列表的不可变性有多深?

java - 用不同的信息写入多个 log4j 文件

java - ReadyStatement 不将数据插入 MySQL DB

java - 将类成员传递给方法

javascript - 根据条件设置多个 const 值

python - 理解什么样的对象可以是字典键

java - 如何更改 JPA - Spring Framework 数据库中的顺序

java - Android 弹出窗口与 Android 对话框