在java中使类不可变

为了使类不可变,我能做的是:

1)让课程最终
2)不提供制定者
3)将所有变量标记为final

但是如果我的类有另一个类的另一个对象,那么somone可以改变该对象的值

class MyClass{ final int a; final OtherClass other MyClass(int a ,OtherClass other){ this.a = a; this.other = other; } int getA(){ return a; } OtherClass getOther(){ return other; } public static void main(String ags[]){ MyClass m = new Myclass(1,new OtherClass); Other o = m.getOther(); o.setSomething(xyz) ; //This is the problem ,How to prevent this? } } 

A)使OtherClass不可变的

要么

B)不允许直接访问OtherClass对象,而只提供getter作为代理。

编辑添加:可以制作OtherClass的深层副本并返回副本而不是原始副本,但这通常不是您在Java中所期望的行为类型。

从API用户的角度来看,最好考虑不变性。 因此,您的对象API需要满足以下两个条件:

  1. 外部用户无法更改对象的值
  2. 保证用户在将来读取或使用对象的值时,将获得相同的结果

重要说明 :事实上,只要从API用户的角度看它是一个不可变对象,就可以在不可变对象中包含可变数据。 例如,考虑java.lang.String:虽然它通常被认为是权威的不可变类,但事实上它确实有一个可变的内部字段来缓存hashCode(很多人都不知道这个!)。

因此,要解决您的问题,如果您希望在不可变对象中包含另一个(可变)对象,那么您通常需要执行以下一项或多项操作:

  • 保证没有其他人可以改变可变对象的价值。 通常这意味着确保没有其他人可以引用可变对象,所以这通常只有在您自己创建对象而不是从外部接受引用时才有可能。
  • 获取可变对象的防御性深层副本 ,并且不要分发对新副本的引用。 仅允许在公共API中读取新副本的操作。 如果你需要分发对这个对象的引用,那么你需要采取另一个防御性副本(以避免分发对内部副本的引用)。
  • 对可变对象使用不可变包装器 。 类似Collections.unmodifiableList的东西。 如果您想要分发对内部可变对象的引用但不希望冒被修改的风险,这将非常有用。

所有这些解决方案都有点hacky – 总体上更好的解决方案是避免在不可变对象中使用可变对象。 从长远来看,它会遇到麻烦,因为迟早会有一个可变引用漏掉,你将会发现一个非常难以找到的bug。 您最好转向完全不可变对象的层次结构(Scala和Clojure等语言采用的方法)

我假设OtherClass(顺便说一下你说的是其他)是一个你无法控制的类,或者必须有一个setter。

如果无法删除getOther ,请将其更改为getOtherView并返回其他的只读视图。 所有get方法都有包装器,但没有set方法。

从你的吸气者那里返回深层克隆。 你可能会发现这不是一件容易的事。

不可变类中引用的所有对象都应该是不可变的,或者至少被封装为私有,并确保它们不被修改(不在类的方法内部,绝对不是从外部)。 例如,如果您遇到这种情况:

 public class MyImmutable { private MutableClass mutableObject; } 

…您无法提供getMutableObject()方法,因为这样做会为外部修改打开大门,如下所示:

 myImmutable.getMutableObject().setSomeAttribute(newValue); 

作为上述的一个特例,所有集合和/或映射都应该是不可变的,使用Collections类中的ummodifiableXXX()方法。

你不能(合理地)在java中停止它。 如果你无法控制其他类,有办法有效地获得不可变行为,但在实践中它可能非常昂贵。 基本上,您必须始终在任何公共方法返回值中返回该类的副本。 (jdk实际上与TimeZone类有这个问题)。

但是如果我的类有另一个类的另一个对象那么,somone可以改变该对象的值…

Java对象不是原始的。 如果将基元标记为final,则分配后不能更改其值。 但是,对象内容不能是最终的,只有对象引用才是最终的。 所以你不能用这种方式制作一个物体。

一种解决方案可能是放弃所有setter / mutator方法,这些方法可以更改对象的特定字段并以只能访问它们的方式封装它们,而不是更改它们。

可以通过以下方式在java中创建不可变类

1.不要提供setter方法。

2.使所有领域都是最终的和私人的。

3.使class级成为最终。