带getter的公共类只是一个Immutable类吗?

时间:2014-04-11 19:47:00

标签: java immutability

下面的类是不可变的吗?如果没有,为什么?

如何更改字段?

public class Student {

    private String name;
    private String age;

    public Student(String name, String age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public String getAge() {
        return age;
    }

}

4 个答案:

答案 0 :(得分:2)

@fge答案可能更完整。但我不知道不可变的必须是线程安全的。对我来说,它们是不同的属性,即使将它们联系起来也是有意义的。

然而,对于不可变的可变性方面,是的,你的班级是。

确实,我不知道这个概念是否存在,但我会说你的课程是“不可改变的”#34; :不仅你不能改变其属性的引用(我称之为shallow immutability),但你不能改变它的属性值,因为它们本身是不可变的。

---- UPDATE

我错了。

我阅读了约书亚布洛赫在Effective Java中给出的定义,但遗漏了以下内容(其他人提到):

  • 该类应该是final,或者有最终方法来阻止子类使实例看起来像状态已经改变。
  • 所有字段都应该是最终的,以获得正确的线程安全性

所以正确答案是:

public final class Student {

    private final String name;
    private final String age;

    public Student(String name, String age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public String getAge() {
        return age;
    }
}

答案 1 :(得分:2)

它不是一成不变的,因为它可以被子类化(类和getter都不是最终的)。子类可以覆盖getter并暴露可能发生变化的状态。另一个持有Student引用的类无法保证其状态不会发生变化,因为可能存在可变子类。

不相关的是这些字段不是final或volatile。因此,无法保证另一个线程会看到最新值。

答案 2 :(得分:1)

从技术上讲,除非您执行以下操作,否则它不是永久性的:

  • 创建类final,否则我可以编写一个更改类的行为的子类,即使其“行为”像一个可变类使所有的getters最终使构造函数变为私有并提供静态工厂方法,因此无法有效地进行子类化;
  • 创建字段final,因此它们是线程安全的。

作为参考,请参阅this page或查看Effective Java

答案 3 :(得分:1)

让我们看看开源工具Mutability Detector对您的Student课程的看法: (免责声明:我是作者)

结果:

Expected: org.mutabilitydetector.stackoverflow.Student to be IMMUTABLE
 but: org.mutabilitydetector.stackoverflow.Student is actually NOT_IMMUTABLE
Reasons:
    Can be subclassed, therefore parameters declared to be this type could be mutable subclasses at runtime. [Class: org.mutabilitydetector.stackoverflow.Student]
    Field is not final, if shared across threads the Java Memory Model will not guarantee it is initialised before it is read. [Field: name, Class: org.mutabilitydetector.stackoverflow.Student]
    Field is not final, if shared across threads the Java Memory Model will not guarantee it is initialised before it is read. [Field: age, Class: org.mutabilitydetector.stackoverflow.Student]

让我们看看两种原因,解释它们的含义:

  • "可以是子类..." 这意味着您的对象的使用者,例如其他类或库,例如,接受它作为参数,不能相信实例是不可变的,因为在运行时,该对象可以是一个可变的子类。这可以通过创建类final或通过不提供公共构造函数来修复。如果您不将类导出到其他用户,并且可以保证代码库中没有子类,则此问题就会消失。
  • "字段不是最终的..." 虽然字段无法更改其引用,但此对象的用户可能会观察到它的更改。由于Java内存模型的语义,您必须确保安全地发布对象,否则它可能看起来像一个字段没有在一次调用中设置它的引用,然后在后续调用中设置它。解决此问题的最简单方法是将字段标记为final,尽管还有其他方法可以实现相同的效果。如果你只是在单线程执行中运行,那么这个问题就会消失。

使用Mutability Detector here查看产生结果的测试用例。

虽然这个答案与Snicolas的结果相同,但我认为值得向您展示一个可以回答相同问题的自动化工具。