13

我想拥有像这样的不可变 Java 对象(高度简化):

class Immutable {

    protected String name;

    public Immutable(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

}

在某些情况下,对象不仅应该是可读的,而且应该是可变的,所以我可以通过继承添加可变性:

public class Mutable extends Immutable {

    public Mutable(String name) {
        super(name);
    }

    public void setName(String name) {
        super.name = name;
    }

}

虽然这在技术上很好,但我想知道它是否符合 OOP 和继承,即 mutable 也是不可变类型的。我想避免UnsupportedOperationException像 Java 集合 API 那样抛出不可变对象的 OOP 犯罪。

你怎么看?还有其他想法吗?

4

5 回答 5

14

避免称父类为“不可变”,因为它在子类中成为谎言——如果你确实想让一个类不可变,它也应该是最终的,以防止出现这个问题。

Joda Time 使用“ReadableXXX”来给出“这个类只提供读访问;其他子类可能是可变的”的想法。我不确定我是否喜欢这个想法,但我不能说我见过很多替代方案。

基本上问题在于表达否定 -Immutable描述你不能做什么(改变它)并且不能在子类中明智地强制执行。(即使其中的字段Immutable是最终的,它也不会阻止子类拥有自己的可变字段。)

于 2010-03-22T16:26:10.260 回答
5

您的子类很糟糕,因为它违反了Liskov 替换原则。不要这样做。

于 2010-03-22T16:39:06.980 回答
5

我建议你应该有一个可继承的基“ReadableFoo”类、一个派生的密封 ImmutableFoo 类和其他派生的 MutableFoo 类。不关心 Foo 是否可变的代码可以接受 ReadableFoo。想要保证不会更改的 Foo 的代码可以接受 ImmutableFoo。可能需要更改 Foo 的代码可以接受 MutableFoo。

请注意,ImmutableFoo 和 MutableFoo 的构造函数通常应该接受 ReadableFoo。这样,任何 Foo 都可以转换为可变或不可变版本。

于 2011-01-23T02:55:13.040 回答
3

不可变类应该final准确地避免可变子类型。

允许不可变类的子类型打破不可变契约使得首先让该类不可变变得毫无意义。从某种意义上说,Java 允许您这样做可能是合法的(语言中不强制执行不变性),但这样的类并不是真正不可变的,只要它可以被子类化。

这就是为什么 String 是最终的。

于 2010-03-22T16:25:15.637 回答
1

我发现你的代码很好奇。

为了实现这种不可变行为,我宁愿依赖一个Immutable接口,只提供 getter 方法,而对象包含两者。这样,依赖于不可变对象的操作会调用接口,而其他操作会调用对象。

而且,如果您真的不希望将不可变对象转换为可变对象,则可以使用代理和所有企业工具(方面等)。但通常,依靠其他开发人员的善意是让他们为自己的错误负责的好方法(比如将不可变的内容转换为可变的)。

于 2010-03-22T16:31:45.567 回答