【问题标题】:Can a class (an instance of the class), containing Random object variable be immutable?包含随机对象变量的类(类的实例)可以是不可变的吗?
【发布时间】:2011-08-08 09:06:18
【问题描述】:

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

这里提出了一个问题,在我看来,状态和对象包含的数据是不同的东西。 也许状态意味着通过 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);
 }
}

【问题讨论】:

    标签: java immutability


    【解决方案1】:

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

    一个很好的例子是java.lang.String,它经常被引用为不可变类的典型例子。它有一个非final 字段,即hashhash 保存哈希码,但默认为 0。第一次调用 hashCode() 时会延迟计算哈希码并将其缓存在该字段中。这样String 对象的 internal 状态可以改变,但 observable 状态永远不会改变(因为hashCode() 总是返回相同的值,无论它是否计算或仅返回缓存值)。

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

    我想说最后一个样本 (Obj4) 肯定是可变的,因为每次读取它时(即每次调用 getRnd() )。

    所以,回答标题中的问题:是的,如果 Random 对象的状态在类本身。

    【讨论】:

    • 第一个告诉我缓存不起作用的 String 文字的人的奖励积分(即 hashCode() 每次都会重新计算它)。
    • 这是其中之一:“\u0000”。或任何将导致零散列码:)
    • 一个更简单的解决方案是""。但我真的对“可读”的非空String 感兴趣(即它使用实际字母,而不仅仅是 U+0000)。
    【解决方案2】:

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

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

    如果对象在构造后更改其状态,我不会将其声明为不可变,即使它必须设置器。

    【讨论】:

    • 那么String呢?它没有设置器,但有 状态可以更改(OpenJDK 实现中的字段hash)。
    • 同意可观察状态。
    【解决方案3】:

    如果引用被识别为identifying,而不是holding,那么不可变对象持有甚至公开对任意类型对象的引用是完全合法的,有问题的对象。例如,将对象引用视为类似于 VIN(车辆识别号——一个唯一标识车辆的字母数字字符串,至少是在美国制造或进口到美国的车辆),并想象一家维修店可能会保留一个它服务过的汽车的 VIN 列表。汽车本身几乎不具备不可变对象的资格,但该列表不会包含汽车——它会识别汽车。人们无法查看 VIN 并知道汽车在维修时是什么颜色,但当汽车进入商店时,我们可以使用 VIN 列表来确定该汽车是否曾经访问过。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-07
      • 1970-01-01
      • 1970-01-01
      • 2013-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-14
      相关资源
      最近更新 更多