如何将Java中的Tutor类改造为不可变类?
嘿,我来帮你把这个Tutor类改成真正的不可变类!你已经想到给name加final是个好开头,但还有几个关键细节要处理——尤其是集合和那个可变的Student类的问题,毕竟题目说不能改Student类,咱们得在这个限制下做文章。
第一步:先把基础的不可变性问题搞定
- 给
name字段加上final修饰符:原代码里name没有final,这意味着后续可以给它重新赋值,完全不符合不可变类的要求,必须加上。 - 先修正代码里的语法错误:你写的代码里变量名带了引号(比如
private String "name";),这是编译不通过的,得把引号去掉,改成正常的变量名。
第二步:构造器里必须做防御性拷贝
原构造器直接把传入的Student[]数组元素加到集合里,但这里有个大坑:外部调用者手里还攥着数组里每个Student对象的引用,他们随便调用setName或者setCourse就能修改这些对象的属性,这会直接破坏Tutor的内部状态,完全违背了不可变性。
因为不能修改Student类,咱们只能退而求其次:为每个传入的Student创建一个副本,把原对象的属性复制到新实例里。这样外部修改原来的Student,就和Tutor内部的对象没关系了。
修改后的构造器应该是这样:
public Tutor(String name, Student[] students) { this.name = name; Set<Student> copiedStudents = new HashSet<>(); for (Student original : students) { // 把每个原Student的属性复制到新对象里 copiedStudents.add(new Student(original.getName(), original.getCourse())); } // 用不可变集合包装一下,确保集合本身不能被添加/删除元素 this.tutees = Collections.unmodifiableSet(copiedStudents); }
这里tutees已经是final的了,所以一旦赋值就不能再改,完美符合不可变的要求。
第三步:getter方法也要防泄露
你原来用Collections.unmodifiableSet(tutees)是对的,这样外部拿不到能修改集合结构的引用,但还有个问题:即使集合不可变,外部拿到里面的Student对象引用后,还是能调用set方法修改属性。
所以咱们在getTutees里也要做一次拷贝——返回的集合里的每个Student都是新的副本。这样外部修改拿到的Student,根本碰不到Tutor内部的真实数据:
public Set<Student> getTutees() { Set<Student> resultSet = new HashSet<>(); for (Student s : tutees) { resultSet.add(new Student(s.getName(), s.getCourse())); } return Collections.unmodifiableSet(resultSet); }
最后,整合出完整的代码
把这些修改拼起来,就是一个真正的不可变Tutor类了:
// 这是不能修改的原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; } } // 改造后的不可变Tutor类 public final class Tutor { private final String name; private final Set<Student> tutees; public Tutor(String name, Student[] students) { this.name = name; Set<Student> copiedStudents = new HashSet<>(); for (Student original : students) { copiedStudents.add(new Student(original.getName(), original.getCourse())); } this.tutees = Collections.unmodifiableSet(copiedStudents); } public Set<Student> getTutees() { Set<Student> resultSet = new HashSet<>(); for (Student s : tutees) { resultSet.add(new Student(s.getName(), s.getCourse())); } return Collections.unmodifiableSet(resultSet); } public String getName() { return name; } }
再啰嗦几句关键的点
- 不可变类的核心原则:类加
final(防止被继承篡改)、所有字段private final(防止重新赋值)。 - 防御性拷贝是重中之重:不管是输入的数组还是里面的可变对象,都要拷贝,切断外部和内部的引用关联。
- 可变元素的处理:因为Student是可变的,所以不仅要保护集合的结构,还要保护集合里的每个元素,避免外部通过引用修改它们。
内容的提问来源于stack exchange,提问作者mike231
相关产品推荐
相关产品推荐

