我想拥有这样的不可变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也是类型不可变的。我想避免OOP犯罪为不可变对象抛出UnsupportedOperationException
,就像Java集合API那样。
答案 0 :(得分:14)
避免调用父“Immutable”因为它成为子类中的谎言 - 如果你做想要使一个类不可变,那么它也应该是最终的,以防止这个问题。
Joda Time使用“ReadableXXX”来表示“此类仅提供读访问权限;其他子类可能是可变的”。我不确定我是否喜欢这个想法,但我不能说我见过很多替代品。
基本上问题在于表达否定 - Immutable
描述你不能做什么(改变它)并且不能在子类中明智地强制执行。 (即使Immutable
中的字段是最终字段,也不会阻止子类具有自己的可变字段。)
答案 1 :(得分:4)
我建议您应该有一个可继承的基础“ReadableFoo”类,派生的密封ImmutableFoo类和其他派生的MutableFoo类。不关心Foo是否可变的代码可以接受ReadableFoo。想要保证不会改变的Foo的代码可以接受ImmutableFoo。需要更改Foo的代码可以接受MutableFoo。
请注意,ImmutableFoo和MutableFoo的构造函数通常应该接受ReadableFoo。这样,任何Foo都可以转换为可变或不可变的版本。
答案 2 :(得分:3)
不可变类应该final
精确地避免可变子类型。
允许一个不可变类的子类型来破坏不可变的契约,这使得该类首先成为不可变的是毫无意义的。在Java允许你这样做的意义上它可能是合法的(在语言中不强制实现不变性),但只要它可以被分类,这样的类就不是真正的不可变的。 / p>
这就是String是最终的原因。
答案 3 :(得分:2)
你的子类很糟糕,因为它违反了Liskov substitution principle。不要这样做。
答案 4 :(得分:1)
我觉得你的代码很奇怪。
为了实现这样的不可变行为,我宁愿依赖Immutable
接口,只提供getter方法,而对象包含两者。这样,依赖于不可变对象的操作就会调用接口,而其他操作则会调用该对象。
而且,如果您真的不希望将不可变对象转换为可变对象,则可以使用代理和所有企业工具(方面等)。但通常情况下,依靠其他开发者的善意是让他们对错误负责的一种甜蜜方式(比如将不可变的东西变成可变的)。