【问题标题】:Immutable class in JavaJava中的不可变类
【发布时间】:2015-03-11 10:52:25
【问题描述】:

根据许多文档,我已经看到不可变类应该具有以下特性:

  1. 类应该是最终的

  2. 所有方法都应该是final

  3. 所有变量都应该是final

  4. 不应有任何二传手

但我的问题是:

  1. 如果我有一个只有最终变量的类怎么办?

  2. 如果我也有设置器,我无​​法更改对象的状态,因为我拥有所有最终变量。那么这将如何影响不变性呢?

  3. 在这种情况下继承如何改变对象状态?

【问题讨论】:

  • 附加点 - 所有字段也必须是不可变的。想象一下public final String[] myArray - 您可以轻松地从外部更改内容
  • 最终类只有种最终方法,因为您不能继承和覆盖它们。
  • 如果你的类是最终类,则不允许继承
  • @binkan ,如果我也覆盖了行为,引用也会改变。因此我的对象不受影响。那么不变性如何影响我的对象状态?
  • 你的 getter 应该返回所有返回对象的克隆。

标签: java class immutability final


【解决方案1】:

1.如果我有一个只有final 变量的类怎么办?

这会让你走得更远,但不是一路走来。这些变量的类型也需要是不可变的。例如考虑

class MyImmutableClass {
    // final variable, referring to a mutable type
    final String[] arr = { "hello" };

    // ...
}

这允许某人做

myImmutableObject.arr[0] = "world";

并有效地改变不可变类的对象。

另外,建议禁止扩展类(因为没有办法强制子类不可变)。请参阅下面对第三个问题的回答。

  1. 如果我也有设置器,我无​​法更改对象的状态,因为我拥有所有最终变量。那么这将如何影响不变性。

没错。如果所有变量都是 final 的,那么标准的 setter 方法就不能存在。

  1. 在这种情况下继承如何改变对象状态?

子类不能改变超类的 final 字段的状态。但是继承还有另一个问题。

如果您有一个由Dog 子类化的不可变Animal,并且Dog 有一个可以改变对象的setDogsName 方法,那么实际上您可能有Animal 对象(类型为Dog ) 实际上是可变的。

换句话说,如果不可变类开放用于扩展,那么不可变性的大部分(全部?)好处都会丢失:例如,如果您收到Animal 作为方法的参数,则不能假设它是不可变的。您不能安全地将 Animal 对象作为键放在哈希映射等中。


基本上原始语句有点多余,这就是为什么我认为它们有点混乱:

  • final 类无法扩展,因此将方法也标记为final 是多余的
  • 如果所有变量都是最终变量,那么说不应该有 setter 有点多余。

此外,这些是足够约束,但不是必要。例如,您可以拥有没有最终变量/最终字段类型的不可变类,只要它们是私有的、从不在内部更改并且从不泄露给外部人员。

【讨论】:

  • 为了保证第 2 点的正确性,对可变字段的引用不得泄露 :)
  • 不确定你的意思。第 2 点谈到了二传手。第 1 点解决了我相信的领域的可变性。 (反正最后加了一句:-)
【解决方案2】:

如果我也有设置器,我不能改变对象的状态,因为 我有所有的最终变量。那么这将如何影响不变性。

final 处于 reference 级别,immutability 处于 instance 级别。

class someMutableClass{
final List someList;
}

在上面的代码中。如果列表的引用转义,那么任何人都可以这样做: someList.add(someValue)

但他们做不到:

someList=someOtherList;

这就是区别。

在这种情况下继承如何改变对象状态?

子类可以访问父类的某些字段,然后对其进行更改。您可以使父类引用指向子类对象并修改其字段。因此,为了确保不变性,您必须确保子类不会更改父类中的任何内容。所以让它final

【讨论】:

    【解决方案3】:

    一个不可变的类是你无法改变的。实现不变性是消除改变对象状态的可能方式的问题。这可以通过结构和行为手段的结合来实现。


    但首先,让我们看看“应该有”的列表:

    • “类应该是最终的” - 这可能是可取的,但它可能不是绝对必要的……甚至是不可取的。即使某些子类的实例是可变的,类的实例1 也可以是不可变的。这完全取决于哪些类需要是不可变的,这取决于上下文。

    • “所有方法都应该是最终的”- 既非必要也不充分。如果类是final,那么它是不必要的。如果类不是final,那么它是不够的。 (您可以在子类中添加具有不同签名的方法。)

    • “所有变量都应该是最终的”- 既非必要也不充分。你可以有一个字段不是final的不可变类,以及一个字段都是final的可变类。

    • “不应该有任何 setter” - 这取决于您所说的“setter”是什么意思,但再一次,这既不是必要的(对于某些可变性模型)也不是充分的。

      李>

    您的问题:

    1) 如果我有一个只有最终变量的类怎么办?

    这不足以保证不变性。例如,如果最终变量之一是数组,则可以更改该数组的状态,从而改变整个对象的状态。这可以通过 setter(或类的任何其他方法)来完成,或者如果该类是“泄漏抽象”,那么它可以由外部代码来完成。

    2) 如果我也有设置器,我无​​法更改对象的状态,因为我拥有所有最终变量。那么这将如何影响不变性。

    见上文。将所有字段声明为 final 并不能保证不变性。 setter 可以更改对象的可变 组件 的状态。

    3) 在这种情况下,继承如何改变对象状态?

    它不能1。但这不是重点。

    创建不可变类final 的原因是为了阻止某人创建该类的可变子类

    为什么这很重要?

    好吧,假设一个方法要求参数是不可变的(例如为了安全)。如果您使用不是 final 的不可变类声明它,那么有人会创建一个可变子类并传递它的实例而不是原始的不可变类。

    这就是为什么(例如)String 类是final 的主要原因。


    1 - 我需要对此进行限定。这取决于我们讨论的是类为A 的实例,还是与A 类型兼容的实例。我说的是前者。存在可变子类这一事实确实会影响其类为 A 的实例的可变性。

    【讨论】:

    • 一个类的实例可以是不可变的,即使某些子类的实例是可变的。 - 嗯。 (可变)Dog 可以是(不可变)Animal 的一个实例,所以这对我来说听起来有点牵强。
    • 见脚注。这取决于您是在谈论dog instanceof Animal 还是dog.getClass() == Animal.class
    • 根据标准术语,Dog 实例确实是Animal 实例。然而,这里的关键点是,如果可以对不可变对象进行子类化,那么不可变性的许多好处就会丢失。例如,我不能安全地使用 Animal 作为哈希映射中的键,如果它实际上可能是一个可变的 Dog 对象。
    • "失去了不变性的许多好处" ...但不是全部。
    猜你喜欢
    • 2018-10-19
    • 1970-01-01
    • 1970-01-01
    • 2012-01-04
    • 2011-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多