Ana*_*and 76 java final immutability
我读到要在Java中创建一个不可变的类,我们应该执行以下操作,
为什么需要第3步?我为什么要上课final?
tem*_*def 125
如果你没有标记该类final,我可能会突然让你看似不可变的类实际上是可变的.例如,考虑以下代码:
public class Immutable {
private final int value;
public Immutable(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
Run Code Online (Sandbox Code Playgroud)
现在,假设我执行以下操作:
public class Mutable extends Immutable {
private int realValue;
public Mutable(int value) {
super(value);
realValue = value;
}
public int getValue() {
return realValue;
}
public void setValue(int newValue) {
realValue = newValue;
}
public static void main(String[] arg){
Mutable obj = new Mutable(4);
Immutable immObj = (Immutable)obj;
System.out.println(immObj.getValue());
obj.setValue(8);
System.out.println(immObj.getValue());
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,在我的Mutable子类中,我已经重写了getValue读取在我的子类中声明的新的可变字段的行为.因此,您的类最初看起来是不可变的,实际上并不是一成不变的.我可以Mutable在Immutable期望对象的任何地方传递此对象,这可能会做非常糟糕的事情来代码,假设对象是真正不可变的.标记基类final可防止这种情况发生.
希望这可以帮助!
Lau*_*ves 39
相反的是,许多人认为,做一个不变类final是不必需的.
创建不可变类的标准参数final是,如果不这样做,则子类可能会增加可变性,从而违反了超类的约定.该类的客户将承担不变性,但当从它们下面突变出来时会感到惊讶.
如果将此参数置于其逻辑极值,则应该生成所有方法final,否则子类可以以不符合其超类合同的方式覆盖方法.有趣的是,大多数Java程序员认为这是荒谬的,但对于不可变类应该是这样的想法是不行的final.我怀疑它与Java程序员有关,一般而言,对于不可变性的概念并不完全感到满意,也许是某种与finalJava中关键字的多重含义有关的模糊思考.
符合您的超类合同不是可以或应该始终由编译器强制执行的.编译器可以强制执行合同的某些方面(例如:最小的方法集和类型签名),但是编译器无法执行典型合同的许多部分.
不变性是一个阶级合同的一部分.这是从一些东西的人更习惯有点不同,因为它说什么类(以及所有子类),不能做什么,而我认为大多数Java(一般OOP)的程序员往往会去想合同的有关一个班级可以做什么,而不是它不能做什么.
不变性也不仅仅影响一个方法 - 它影响整个实例 - 但这equals与hashCodeJava工作方式和方法并没有太大的不同.这两种方法都有一份具体的合同Object.这份合同非常仔细地列出了这些方法无法做到的事情.该子合约在子类中更具体.覆盖equals或hashCode以违反合同的方式很容易.事实上,如果你只重写这两种方法中的一种而没有另一种方法,那么很可能你违反了合同.所以应该equals和hashCode已经宣布final在Object避免这种情况?我想大多数人都认为他们不应该这样做.同样,没有必要创建不可变类final.
也就是说,你的大多数课程,不论是否一成不变,都应该是final.请参阅Effective Java Second Edition第17项:"继承的设计和文档,否则禁止它".
因此,步骤3的正确版本将是:"使类最终,或者在设计子类时,清楚地记录所有子类必须继续是不可变的."
Jus*_*hms 20
不要将整个班级标记为最终.
如某些其他答案所述,允许扩展不可变类是有正当理由的,因此将该类标记为final并不总是一个好主意.
最好将您的属性标记为私有和最终,如果您想保护"合同",请将您的获取者标记为最终.
通过这种方式,您可以允许扩展类(甚至可能是可变类),但是类的不可变方面是受保护的.属性是私有的,无法访问,这些属性的getter是final,无法覆盖.
使用不可变类的实例的任何其他代码将能够依赖于类的不可变方面,即使它传递的子类在其他方面是可变的.当然,因为它需要你的课程的一个实例,它甚至不知道这些其他方面.
如果您没有将其定为最终版本,我可以扩展它并使其不可变。
public class Immutable {
privat final int val;
public Immutable(int val) {
this.val = val;
}
public int getVal() {
return val;
}
}
public class FakeImmutable extends Immutable {
privat int val2;
public FakeImmutable(int val) {
super(val);
}
public int getVal() {
return val2;
}
public void setVal(int val2) {
this.val2 = val2;
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我可以将 FakeImmutable 传递给任何期望 Immutable 的类,并且它不会按照预期的契约运行。
| 归档时间: |
|
| 查看次数: |
23433 次 |
| 最近记录: |