【问题标题】:What is a class invariant in Java?Java中的类不变量是什么?
【发布时间】:2012-02-12 17:04:56
【问题描述】:

我搜索了这个主题,但除了Wikipedia,我没有找到任何其他有用的文档或文章。

谁能用简单的语言向我解释它的含义或向我推荐一些易于理解的好文档?

【问题讨论】:

  • +1 这个问题,因为维基百科页面有一个非常好的例子,我不知道你可以做 - 它甚至有例子。他们的解释比我能为你做的要好;这很简单。
  • 如果您对 w.r.t. 的不变量感兴趣。 Java,也许你会对contracts for Java感兴趣。
  • 更简单的解释——stackoverflow.com/questions/112064/what-is-an-invariant?rq=1>

标签: java terminology invariants


【解决方案1】:

这并不意味着任何关于 Java 的特殊含义。

类不变量只是一个属性,它始终适用于类的所有实例,无论其他代码做什么。

例如,

class X {
  final Y y = new Y();
}

X 具有类不变量,即有一个 y 属性,它永远不是 null,它的值类型为 Y

class Counter {
  private int x;

  public int count() { return x++; }
}

这未能保持两个重要的不变量:

  1. count 永远不会返回负值,因为可能存在下溢。
  2. count 的调用是严格单调递增的。

修改后的类保留了这两个不变量。

class Counter {
  private int x;

  public synchronized int count() {
    if (x == Integer.MAX_VALUE) { throw new IllegalStateException(); }
    return x++;
  }
}

...但未能保留调用count 总是正常成功的不变量(不存在TCB 违规),因为count 可能会抛出异常,或者如果死锁它可能会阻塞线程拥有计数器的监视器。

每种带有类的语言都可以很容易地维护一些类不变量,而其他的则不然。 Java 也不例外:

  1. Java 类始终具有或不具有属性和方法,因此接口不变量易于维护。
  2. Java 类可以保护其private 字段,因此依赖私有数据的不变量易于维护。
  3. Java 类可以是最终类,因此可以维护依赖于不存在通过制作恶意子类而违反不变量的代码的不变量。
  4. Java 允许null 值以多种方式潜入,因此很难维护“具有实际值”的不变量。
  5. Java 有线程,这意味着不同步的类难以维护依赖于线程中一起发生的顺序操作的不变量。
  6. Java 有一些例外,这使得维护诸如“返回具有属性 p 的结果或不返回结果”之类的不变量变得容易,但更难维护诸如“始终返回结果”之类的不变量。

† - 外部性TCB违规是系统设计师乐观地认为不会发生的事件。

通常情况下,我们只相信基本硬件在谈论构建在它们之上的高级语言的属性时会像宣传的那样工作,而我们的不变量所持有的论点没有考虑以下可能性:

  • 当程序以代码无法运行的方式运行时,程序员使用调试挂钩来更改局部变量。
  • 您的同行不使用 setAccessible 的反射来修改 private 查找表。
  • Lo​​ki 改变物理特性导致您的处理器错误地比较两个数字。

对于某些系统,我们的 TCB 可能仅包括系统的一部分,因此我们可能不会假设

  • 管理员或特权守护进程不会杀死我们的 JVM 进程,

...但我们可以假设:

  • 我们可以检查点到可靠的事务文件系统。

系统级别越高,其 TCB 通常越大,但您可以从 TCB 中获得的不可靠的东西越多,您的不变量就越有可能保持,您的系统在很长一段时间内就越可靠运行。

【讨论】:

  • “that count 永远不会返回相同的值两次”真的被认为是类不变量吗?
  • @ruakh,这是个好问题。我不太确定。 hashCode 稳定性(对于每个实例 i,i.hashCode() 不会改变)通常称为类不变量,这需要对先前返回的值进行推理,因此说“对于每个实例 i,i.count() not in (previous results of i.count())" 是一个类不变量。
  • @ruakh 这不是纯粹的定义吗?如果我假设这样一个不变量,为什么不呢?它当然可以是一个有趣且重要的保证(例如用于生成唯一 ID)。就我个人而言,我也认为“如果只有单线程代码访问此类,则以下属性将保持”之类的东西很有用,但我不确定是否可以扩展定义,使其只需要在某些情况下保持条件为真。 (考虑到反射,基本上不可能保证任何有趣的事情!)
  • @ruakh - 您可以将其建模为类不变量或方法不变量。无论哪种方式,对其建模都需要对特定对象上的方法的先前调用的概念历史记录。实际上,您甚至可以将其建模为该方法的后置条件;即结果值是以前没有返回的值。
  • @Voo:回复:“那不是纯粹的定义吗?”:当然,但既然这里的问题是,“什么是'类不变量'?”,我认为定义是100% 相关。在可能的范围内,使用明确的例子似乎更可取,或者明确指出不明确的情况。 (顺便说一句,我并没有积极反对这个例子;我只是对此感到惊讶,并询问是为了确定。)
【解决方案2】:

不变量意味着无论发生什么变化或无论谁使用/转换它,都应该坚持其条件。也就是说,一个类的属性,即使经过公共方法的变换,也总是满足或满足某个条件。所以,这个类的客户或用户对这个类及其属性是有保证的。

例如,

  1. 函数参数的条件是,它应该始终 > 0(大于零)或不应该为 null。
  2. 帐户类的 Minimum_account_balance 属性规定,它不能低于 100。因此所有公共函数都应遵守此条件并确保类不变。
  3. 变量之间基于规则的依赖关系,即一个变量的值依赖于另一个变量,所以如果一个变量发生变化,使用一些固定规则,另一个变量也必须改变。必须保留 2 个变量之间的这种关系。如果不是,则违反不变量。

【讨论】:

    【解决方案3】:

    它们是关于实例类必须为真的事实。例如,如果一个类有一个属性 X 并且不变量可以是 X 必须大于 0。据我所知,没有用于维护不变量的内置方法,您必须将属性设为私有并确保您的 getter 和 setter 强制执行不变属性。

    有可用的注释可以使用反射和拦截器检查属性。 http://docs.oracle.com/javaee/7/api/javax/validation/constraints/package-summary.html

    【讨论】:

      猜你喜欢
      • 2010-11-08
      • 1970-01-01
      • 2021-10-04
      • 2018-11-26
      • 2013-05-17
      • 2015-11-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多