【问题标题】:Immutable java objects不可变的 java 对象
【发布时间】:2012-07-16 06:11:41
【问题描述】:

今天我在阅读一些关于 BigDecimal 类的文档时, 我偶然发现了一个基本属性,BigDecimal 类是不可变的。

我该如何向祖母解释不变性的概念?

类的不变性的优缺点是什么?

扩展类可以变得可变吗?

考虑到我想用我的班级扩展 BigDecimal:

`MyBigDecimal extends BigDecimal` 

扩展是否违反了基本的面向对象设计原则?

【问题讨论】:

  • 1. “不变”。 3. 是的,可以。 4. 如果扩展一个类会违反基本的 OOP 原则,那么它就不是 Java 的一个特性。
  • BTW BigDecimal 就像 JDK 中的许多不可变类一样是 final,所以它们不能被扩展和可变。
  • @PeterLawrey BigDecimal 不是最终的,尽管它应该是。
  • @assylias 好点。它的方法也不是最终的,这意味着它们可以更改。
  • @assylias 因此,这可以被视为继续扩展类并最终覆盖它的方法的绿灯?

标签: java design-patterns bigdecimal


【解决方案1】:

我该如何向祖母解释不变性的概念?

检查这个问题:What is meant by immutable

或者来自 Effective Java:

不可变类是不能修改其实例的类。每个实例中包含的所有信息都是在创建时提供的,并且在对象的生命周期内是固定的。


类的不变性的优缺点是什么?

优点:

  • 更容易推断对象的状态,因为只有一个,即在初始化时构建的状态
  • 推论:不可变对象更易于在并发编程中使用,其中状态就是一切

缺点:

  • 当你想改变一个对象的属性时,你需要创建一个新的 = 昂贵的
  • 构造可能更复杂(参见构建器模式)

扩展类可以变得可变吗?

是的 - 这就是为什么不可变类应该成为最终类的原因(或者,将所有构造函数设为私有并提供工厂来创建新对象)。

BigDecimal 是一个很好的例子,说明了在创建不可变类时应该做什么(它可以扩展,这可能会导致您在问题中提到的问题)。

【讨论】:

    【解决方案2】:

    BigDecimal 是一个值类。它代表了来自“现实世界”的价值。让我们以整数为例。 42 的值始终为 42。它的状态不能更改。如果我想要 43,它不是 42 的更改值,而是 43 的值。这个抽象的值概念通过使用不可变类在面向对象的世界中传递。如果您想向现有的数字添加一个数字,它不会更改,但会创建一个包含结果的新不可变对象。

    【讨论】:

      【解决方案3】:

      实例永远不会改变的不可变类。对象的状态是在构造时定义的,之后永远不会改变。

      为了正确地不可变,类不提供任何方式(除了反射)来改变它的状态:没有 setter 方法,没有改变其内部状态的方法,没有允许访问它持有的可变字段的方法。它也应该是 final 的(BigDecimal 应该是),以防止任何其他类扩展它,从而通过添加可变字段使其可变。

      优点很多:

      • 课程通俗易懂
      • 实例本质上是线程安全的
      • 可以缓存实例而无需从缓存中返回副本
      • 实例可以用作 Map 中的键,而不必担心之后被更改

      不违反OO原则:相反,状态完全封装在对象中。

      注意:一些不可变对象会在内部改变它们的状态(例如某些字段的延迟初始化),而不会影响对象的外部可见状态。如果处理得当,它不会改变不可变对象的线程安全性。如果操作不当,可能会使它们成为非线程安全的。

      【讨论】:

      • 检查String.hashCode,它会改变字符串的内部状态——以一种完全非线程安全的方式。
      • 是的,但这是一个令人讨厌的高级技巧。而且它绝对是线程安全的,因为访问缓存值的唯一方法是通过 hashCode() 方法,如果它等于 0,它会以确定的方式重新计算它。并且 int 变量的分配是原子操作。
      • @MarkoTopolnik 它是线程安全的。最坏的情况是计算不止一次。
      • @assylias 是的,从hashCode 合约的角度来看,它是线程安全的。这只是一段有趣的代码。但我的主要观点是,没有要求“没有改变其内部状态的方法”。
      • @MarkoTopolnik:同意。我在我的答案中添加了一条注释以使其更清楚。
      【解决方案4】:

      类的不变性意味着如果创建了对象,则无法更改其内容。

      考虑例如

      String str = "Hello"; // you can not change content Hello to any other string
      

      immutability of a class - Pros. / Cons. of Immutability vs. Mutability 的优缺点

      扩展类可以变得可变吗?

      是的,你可以做到。

      MyBigDecimal extends BigDecimal

      你可以做到BigDecimal不是最终的。

      【讨论】:

      • 其实你可以改变它的内容。如果我没记错的话,字符串就是一个例子(hashCode 的延迟缓存)。只是外在的行为不能改变。
      • @MarkoTopolnik : 正是...:)
      • @MarkoTopolnik 哈希码的延迟缓存是可以的,只要它在外部不可见。字符串的另一个问题是您可以使用 CharSequence 构造函数对其进行变异。有关示例,请参见 this question
      • @assylias 当然可以,我会怀疑String的不变性吗? :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-01
      • 2021-05-04
      • 1970-01-01
      相关资源
      最近更新 更多