包含Random对象变量的类(类的实例)是否可以是不可变的?

时间:2011-08-08 09:06:18

标签: java immutability

不变性的定义表明,在构造之后,对象的状态(其数据)不能被改变。

这里提出了一个问题,在我看来,对象包含的状态和数据是不同的东西。 也许状态意味着通过getter提供的数据?

这并不意味着标记为私有且外部世界不可见的数据,这些数据确实可以改变而不会改变对象的状态。

告诉我这是否正确:

final class Obj1 {
 private final  int i; 
 private final  Random rnd = new Random();
 private int j = rnd.nextInt(1000);
 public Obj1(int i) {
   this.i = i;
  }  
 public getI() {
  j = rnd.nextInt(1000);
  return i;
 }
}

Obj1的实例是一个不可变对象。

final class Obj2 {
 private final int i; 
 private final Random rnd = new Random();
 private int j = rnd.nextInt(1000);
 public Obj1(int i) {
   this.i = i;
 }  
 public getI() {
   return i;
 }
 public getJ() {
   return j;
 }
}

Obj2的实例是一个可变或不可变的对象,为什么?如果每次调用getter时我们在getJ体中得到下一个Random会怎么样?

这样的课怎么样?可变/不可变,为什么?

final class Obj3 {
 private final  Random rnd = new Random();
 private int j = rnd.nextInt(1000);
 public Obj1() {
  }  
 public getJ() {
   return j;
 }
}

这个怎么样?可变/不可变,为什么?

final class Obj4 {
 private final Random rnd = new Random();
 public Obj1() {
  }  
 public getRnd() {
   return rnd.nextInt(1000);
 }
}

3 个答案:

答案 0 :(得分:7)

关于不变性的一个重要观点是,对象的可观察状态不得改变。

一个非常好的例子是java.lang.String,它经常被引用为不可变类的规范示例。它有一个非final字段,即hashhash包含哈希码,但默认为0。在第一次调用hashCode()并在该字段中缓存时,会懒惰地计算哈希码。这样String对象的内部状态可以改变,但可观察状态永远不会改变(因为hashCode()总是返回相同的值,不无论是计算还是仅返回缓存值,都很重要。)

这意味着您提供的前三个示例(Obj1Obj2Obj3)是不可变的:它们没有setter,其他任何东西都无法更改其方法返回的值构造(声明字段final是个好主意,但它不是不可变性的要求)。另请注意,您也可以完全忽略这些类中的Random字段,因为它在构建之后仍未使用。

我会说最后一个样本(Obj4)肯定是可变的,因为每次从中读取时都会改变状态(即下一个getRnd()调用返回的内容)(即每次你致电getRnd())。

所以,回答标题中的问题:是的,如果Random对象的状态不可观察,引用Random对象的类可以是不可变的在班级本身的状态。

答案 1 :(得分:0)

好问题。但这是术语的问题,而不变性更多的是关于如何使用对象。不可变对象的好处:

  • 您可以通过引用传递它们,并确保没有人更改其状态;
  • 您无需考虑同步;
  • 在哈希映射中用作密钥更安全。

如果它在构造后改变状态,我不会将对象声明为不可变的,即使它必须是setter。

答案 2 :(得分:0)

如果引用被识别为标识而不是持有,则不可变对象保持甚至公开对任意类型对象的引用是完全合法的,有问题的对象。例如,将对象引用视为类似于VIN(车辆标识号 - 唯一标识车辆的字母数字字符串,至少是美国制造或进口的车辆),并想象维修车间可能保留一个它所服务的汽车的VIN列表。汽车本身很难成为不可改变的物品,但是列表不能存放汽车 - 它会识别汽车。人们无法看到VIN并且知道汽车在维修时的颜色是什么,但是当汽车进入商店时,可以使用VIN列表来确定汽车是否曾经访问过。