如果我有这样的课程:
public class MyObject {
private int myField = 2;
public void setMyField(int f) {
this.myField = f;
}
}
这个类的对象是否可变? 谢谢!
答案 0 :(得分:10)
当然 - 如果你想要它是不可变的,那么你需要像:
public class MyObject {
private final int myField;
public MyObject(int f) {
myfield = f;
}
public int getMyField() {
return myField;
}
}
答案 1 :(得分:9)
可变对象具有可以更改的字段,不可变对象 在创建对象后没有可以更改的字段。
答案 2 :(得分:6)
您已经有几个答案,其中" 是"。
我想添加" 但" (如果我会大胆,我会说" 不"; - )
是的,此类的对象似乎是可变的,因为它提供了一个更改字段的setter。但是,由于它没有该字段的getter,也没有任何其他getter取决于该字段,并且由于该字段是私有的,因此目前无法读取该状态。
换句话说:对象具有状态,但它不会将任何状态暴露给外部。
我会称这个对象为#34;实际上是不可变的"。
有一些设计模式,其中对象是"有效地不可变",例如"懒惰初始化"一个"不可变对象"。
注意:存在"有效不可变的概念"在Brian Goetz的“Java Concurrency in Practice”第3.5.4节中讨论。
答案 3 :(得分:1)
是的,这个类的对象是可变的。该类的设计者不能通过任何技术手段(在现有的Java中)禁止对象的消费者观察和修改该领域的内容。
私人是关于该字段的预期用途的明确声明的合同 - 该合同可以被破坏,例如用反射。
在创建之后不提供任何更改对象数据的方法也可以是(隐式声明的)有关预期用途和可变性的合同 - 此合同也可以被破坏,就像任何需要两个合规方的合同一样。
编辑:除非有另一方能够强制执行 - 就像在Java中一样,SecurityManager会阻止您在运行时更改 final 字段。
答案 4 :(得分:0)
是 ,您的对象是可变的,因为在使用setter创建实例后,myField
的值可以更改。
使用final
字段可以实现不变性,因为一旦初始化变量,它将不允许您更改变量的值。
@JakubK的回答指出如何让你的班级成为永恒的。
但声明引用final
不会使对象被final
指向。
例如:
class MyObject{
private final List<Integer> list = new ArrayList<Integer>();
public List<Integer> getList(){
return list;
}
}
我可以通过做类似的事情来改变从外部向list
添加新元素
instance.getList().add(1); //mutates the list
此示例不是不可变的,因为List可以被其他人更改。
答案 5 :(得分:0)
要定义某些东西是否可变,必须定义由此封装的状态。如果MyObject
指定其状态包含Reflection将为myField
报告的值,则它是可变的。如果该字段未被指定为对象的可观察状态的一部分,那么它可能被认为是不可变的。
更具体地说,只有当一个人可以在类,任何顺序和任何时间(甚至在多个线程上)执行任何记录操作的组合时,我认为一个类是不可变的,并且没有任何记录的行为受其他任何影响的任何方面。当且仅当该更改会影响另一个方法调用的某些记录的行为方面(对于相同或不同的方法)时,方法可能更改私有字段的内容这一事实是相关的。例如,我会说String.hashCode()
修改hash
字段的事实不会使String
变为可变,因为hashCode()
返回的值不受是否影响该领域先前已经写过。如果一个类有hashCode
方法,如果一个字段为空,则生成一个随机数并将其存储在该字段中,否则直接返回该值,这样的类将是可变的,除非它确保该字段经过测试并设置为原子操作。在没有这种保证的情况下,几乎可以同时调用hashCode()
来产生不同的值,从而将来调用不同的值,这些值与至少其中一个不同(暗示对象的状态)在返回奇数球值的召唤和后来的召唤之间发生了变化。