【问题标题】:Can this class be made more immutable?这个类可以变得更加不可变吗?
【发布时间】:2015-08-20 02:44:13
【问题描述】:
package main;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public final class Tutor { 

    private final String name;          
    private final Set<Student> tutees;            

    public Tutor(String name, Student[] students) {           
        this.name = name;    
        this.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; } 

}  

是否可以做更多的事情来使这个类不可变?字符串已经是不可变的,返回的集合是不可修改的。 tutees 和 name 变量是私有的和最终的。还能做什么?如果使用 Tutor 类的唯一类在包中,我可以将构造函数、getTutees 方法和 getName 方法更改为包私有吗?

编辑:

这是 Student 类,问题要求我描述必要的更改以使 Student 不可变。我已经注释掉了两个 setter 方法,所以我可以使变量成为最终的。这是使它真正不可变的唯一方法吗?

public final class Student {   
    private final String name;   
    private final 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; }  
}

【问题讨论】:

  • 我认为您可能希望在代码审查中使用这个...
  • 如果Student 不可变,则代码的调用者可以稍后对其进行修改。深拷贝数组。
  • 对字符串的修改可能会导致创建新字符串,您可以使用最终的 StringBuffer。
  • 制作包私有的东西与不变性无关。关于这一点:一个类要么是不可变的,要么是不可变的——中间没有。如果您查看您的代码,一旦创建了 Tutor 对象,就无法更改任何内容。但是,这并没有说明 Student 对象,如果 Student 是可变的,它仍然可以通过 getTutees() 方法进行变异。
  • 将构造函数设为私有并实现单例模式。

标签: java immutability mutable defensive-programming defensive-copy


【解决方案1】:

作为一个小优化,您可以使tutees 不可变,因此它甚至不能在Tutor 内部更改。

public Tutor(String name, Student[] students) {
    this.name = name;
    Set<Student> tuts = new HashSet<>();
    for (Student student : students) {
        tuts.add(student);
    }
    this.tutees = Collections.unmodifiableSet(tuts);
}
public Set<Student> getTutees() { return this.tutees; }

短版:

public Tutor(String name, Student[] students) {
    this.name = name;
    this.tutees = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(students)));
}
public Set<Student> getTutees() { return this.tutees; }

【讨论】:

  • 如果在构造此对象时对Student 对象的引用保存在其他地方并且Student 实例是可变的,那么即使您编写了这个类,它仍然是可变的。现在可以查看 Student 的实现,我们可以自信地说您的类在编写时是不可变的。
  • @scottb 是的。如果 Student 是可变的,则无法真正使 Tutor 不可变。即使Student 引用不在其他地方,您的评论也是正确的,因为可以在此处获得参考。
【解决方案2】:

您的班级的不变性仅取决于Student 班级的不变性。如果Student 是不可变的,那么Tutor 是不可变的,反之亦然。确保Tutor 类的不变性不需要其他任何东西。

关于可见性。如果您的类仅在包中使用,则 make 是包私有的(在类级别上)。公开公共方法。

【讨论】:

  • 不变性通常被认为是一种全有或全无的现象,但可能存在灰色阴影。如果Student 实际上是不可变的 ...或...有一个实现合同,在将Student[]添加到此类后不得保留对它的引用...那么它可以将类视为不可变的是适当的。 Collections.unmodifiableList() 等包装器就是这种情况。
  • @scottb 我同意实际上不可变 Student,看不出它与我所说的有何不同。但是当Student 是可变的时,你不能确定它在被getTutees 方法返回后不会发生变异,因为Student 实例是按原样返回的,没有包装或复制。
【解决方案3】:

Immutables 是一个方便的工具包,用于在 Java 中制作不可变对象。如果您使用它构建整个域,它将是不可变的。它排除了“这个对象是否不可变”的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-22
    • 2021-07-10
    • 2021-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-18
    • 1970-01-01
    相关资源
    最近更新 更多