什么是Java中的类不变式?


93

我搜索了该主题,但是除了Wikipedia之外,我没有找到其他有用的文档或文章。

有人可以用简单的词向我解释这意味着什么,还是可以将我引向一些简单易懂的文档?


2
这个问题+1,因为Wikipedia页面上有一个我不知道您可以做的事的很好的例子-甚至有例子。他们的解释比我能为您做的更好。这很简单。
iandisme 2012年


如果您对Java的不变量感兴趣,也许您会对Java的合同感兴趣。
Mike Samuel

Answers:


91

对于Java而言,这并不意味着什么。

类不变式只是一个属性,它始终为一个类的所有实例保存,无论其他代码做什么。

例如,

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

X具有不变的类,即存在一个y属性,并且永远不存在,null并且它具有type值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查找表。
  • Loki改变物理结构会导致处理器错误地比较两个数字。

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

  • 管理员或特权守护程序不会终止我们的JVM进程,

但是我们可以假设

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

系统级别越高,通常其TCB越大,但是从TCB中获得的不可靠信息越多,不变量保持的可能性就越大,从长远来看,系统越可靠。


1
count永远不会两次返回相同的值”真的被视为类不变式吗?
ruakh 2012年

@ruakh,这是一个很好的问题。我不太确定 诸如hashCode稳定性(对于每个实例i,i.hashCode()不变)之类的事情通常称为类不变式,它需要对先前返回的值进行推理,因此似乎可以合理地说“对于每个实例i,i.count()不存在(i.count()的先前结果)”是类不变式。
Mike Samuel

@ruakh那不是纯粹的定义吗?如果我假设这样的不变性,为什么不呢?当然,这可以是一个有趣且重要的保证(例如生成唯一ID的保证)。我个人也认为类似“如果仅单线程代码访问该类,则以下属性将成立”这样的东西有用,但是我不确定是否有可能以这样的方式扩展该定义,即仅在某些情况下必须持有该定义。条件是真实的。(考虑反射,基本上不可能保证没有其他有趣的事情!)
Voo 2012年

1
@ruakh-您可以将其建模为类不变或方法不变。无论哪种方式,对其进行建模都需要在特定对象上对该方法的先前调用的概念历史记录。实际上,您甚至可以将此建模为方法的后置条件。即结果值是以前未返回的值。
史蒂芬·C

@Voo:回复:“那不是纯粹的定义吗?”:当然,但是由于这里的问题是“什么是'类不变式'?”,我认为定义是100%相关的。在可能的情况下,似乎最好使用明确的示例,或者明确地指出非明确幅度的情况。(顺便说一下,(顺便说一句,我并没有积极反对该示例;我只是对此感到惊讶,并要求进行确认。)
ruakh 2012年

20

不变是指无论发生什么变化或使用或转换的人,都应该坚持其条件的事物。也就是说,即使使用公共方法进行了转换,类的属性也始终满足或满足某些条件。因此,可以确保此类的客户端或用户有关该类及其属性。

例如,

  1. 函数参数的条件是,该参数应始终> 0(大于零)或不为null。
  2. 帐户类的minimum_account_balance属性指出,它不能低于100。因此,所有公共函数都应遵守此条件并确保类不变。
  3. 变量之间基于规则的依赖关系,也就是说,一个变量的值依赖于另一个变量,因此,如果使用某些修订规则进行更改,则另一个变量也必须更改。必须保留2个变量之间的关系。如果不是,则违反不变式。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.