Was ist eine Klasseninvariante in Java?


93

Ich habe das Thema gegoogelt, aber außer Wikipedia habe ich keine weiteren nützlichen Dokumentationen oder Artikel gefunden.

Kann mir jemand in einfachen Worten erklären, was es bedeutet, oder mich auf eine nette und leicht verständliche Dokumentation verweisen?


2
+1 für die Frage, weil die Wikipedia-Seite ein wirklich gutes Beispiel für etwas enthält, von dem ich nicht wusste, dass Sie es tun können - es gibt sogar Beispiele. Ihre Erklärung ist jedoch besser als ich es für Sie tun könnte; es ist ziemlich einfach.
Iandisme


Wenn Sie an Invarianten für Java interessiert sind, interessieren Sie sich vielleicht für Verträge für Java .
Mike Samuel

Antworten:


91

Es bedeutet nichts Besonderes in Bezug auf Java.

Eine Klasseninvariante ist einfach eine Eigenschaft, die für alle Instanzen einer Klasse immer gilt, unabhängig davon, was anderer Code tut.

Beispielsweise,

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

X hat die Klasseninvariante, dass es eine yEigenschaft gibt und es ist nie nullund es hat einen Wert vom Typ Y.

class Counter {
  private int x;

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

kann zwei wichtige Invarianten nicht beibehalten

  1. Dies countgibt aufgrund eines möglichen Unterlaufs niemals einen negativen Wert zurück.
  2. Das fordert countstreng monoton zu.

Die modifizierte Klasse behält diese beiden Invarianten bei.

class Counter {
  private int x;

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

Die Invariante, die aufgerufen wird, um countimmer normal erfolgreich zu sein (ohne TCB-Verstöße ) , bleibt jedoch nicht erhalten, da countmöglicherweise eine Ausnahme ausgelöst oder blockiert wird, wenn ein blockierter Thread den Monitor des Zählers besitzt.

Jede Sprache mit Klassen macht es einfach, einige Klasseninvarianten zu pflegen, andere jedoch nicht. Java ist keine Ausnahme:

  1. Java-Klassen haben konsistent Eigenschaften und Methoden oder nicht, sodass Schnittstelleninvarianten einfach zu pflegen sind.
  2. Java-Klassen können ihre privateFelder schützen , sodass Invarianten, die auf privaten Daten basieren, einfach zu pflegen sind.
  3. Java-Klassen können endgültig sein, sodass Invarianten beibehalten werden können, die darauf angewiesen sind, dass kein Code vorhanden ist, der eine Invariante durch Erstellen einer böswilligen Unterklasse verletzt.
  4. In Java können sich nullWerte auf vielfältige Weise einschleichen, sodass es schwierig ist, Invarianten mit "hat einen echten Wert" beizubehalten.
  5. Java hat Threads, was bedeutet, dass Klassen, die nicht synchronisiert werden, Probleme haben, Invarianten zu verwalten, die auf sequentiellen Operationen in einem Thread beruhen, die zusammen stattfinden.
  6. Java hat Ausnahmen, die es einfach machen, Invarianten wie "gibt ein Ergebnis mit der Eigenschaft p zurück oder gibt kein Ergebnis zurück" zu pflegen, aber schwieriger, Invarianten wie "gibt immer ein Ergebnis zurück" zu pflegen.

† - Eine Externalität oder TCB- Verletzung ist ein Ereignis, von dem ein Systemdesigner optimistisch annimmt, dass es nicht eintreten wird.

Normalerweise vertrauen wir nur darauf, dass die grundlegende Hardware wie angekündigt funktioniert, wenn wir über Eigenschaften von darauf basierenden Hochsprachen sprechen, und unsere Argumente, die Invarianten vertreten, berücksichtigen nicht die Möglichkeit von:

  • Ein Programmierer, der Debug-Hooks verwendet, um lokale Variablen zu ändern, während ein Programm auf eine Weise ausgeführt wird, die Code nicht kann.
  • Ihre Kollegen verwenden Reflection with nicht setAccessible, um privateNachschlagetabellen zu ändern .
  • Loki verändert die Physik und führt dazu, dass Ihr Prozessor zwei Zahlen falsch vergleicht.

Bei einigen Systemen enthält unser TCB möglicherweise nur Teile des Systems, sodass wir dies möglicherweise nicht annehmen

  • Ein Administrator oder ein privilegierter Daemon beendet unseren JVM-Prozess nicht.

aber wir könnten das annehmen

  • Wir können auf ein zuverlässiges Transaktionsdateisystem verweisen.

Je höher ein System ist, desto größer ist normalerweise sein TCB. Je unzuverlässiger Sie jedoch aus Ihrem TCB herausholen können, desto wahrscheinlicher ist es, dass Ihre Invarianten halten, und desto zuverlässiger ist Ihr System auf lange Sicht.


1
Wird "das countnie zweimal denselben Wert zurückgibt" wirklich als Klasseninvariante betrachtet?
Ruakh

@ruakh, das ist eine gute Frage. Ich bin mir nicht ganz sicher. Dinge wie die hashCode-Stabilität (für jede Instanz i ändert sich i.hashCode () nicht) werden oft als Klasseninvarianten bezeichnet, die eine Überlegung über zuvor zurückgegebene Werte erfordern. Daher erscheint es vernünftig zu sagen, dass "für jede Instanz i i.count () not in (vorherige Ergebnisse von i.count ()) "ist eine Klasseninvariante.
Mike Samuel

@ruakh Ist das nicht eine reine Definitionssache? Wenn ich eine solche Invariante postuliere, warum nicht? Dies kann sicherlich eine interessante und wichtige Garantie sein (z. B. zum Generieren eindeutiger IDs). Persönlich denke ich auch, dass etwas wie "Wenn nur Code mit einem Thread auf diese Klasse zugreift, gelten die folgenden Eigenschaften" nützlich ist, aber ich bin mir nicht sicher, ob es möglich ist, die Definition so zu erweitern, dass sie nur unter bestimmten Bedingungen gelten muss Bedingungen sind wahr. (Und in Anbetracht der Reflexion ist es im Grunde unmöglich, etwas Interessantes zu garantieren!)
Voo

1
@ruakh - Sie können es als Klasseninvariante oder Methodeninvariante modellieren. In beiden Fällen erfordert die Modellierung eine konzeptionelle Historie früherer Aufrufe der Methode für ein bestimmtes Objekt. Sie können dies sogar als Nachbedingung für die Methode modellieren. dh, dass der Ergebniswert einer ist, der zuvor nicht zurückgegeben wurde.
Stephen C

@Voo: Re: "Ist das nicht eine reine Definitionssache?": Sicher, aber da die Frage hier lautet: "Was ist eine 'Klasseninvariante'?", Denke ich, dass die Definition 100% relevant ist. Es erscheint, soweit möglich, vorzuziehen, eindeutige Beispiele zu verwenden oder Fälle von nicht eindeutiger Vorgehensweise explizit hervorzuheben. (Ich bin übrigens nicht aktiv mit dem Beispiel nicht einverstanden; ich war nur überrascht und fragte, um sicherzugehen.)
Ruakh

20

Invariante bedeutet etwas, das an seinen Bedingungen festhalten sollte, unabhängig davon, was sich ändert oder wer es verwendet / transformiert. Das heißt, eine Eigenschaft einer Klasse erfüllt oder erfüllt immer eine bestimmte Bedingung, selbst nachdem Transformationen mit öffentlichen Methoden durchgeführt wurden. Der Client oder Benutzer dieser Klasse ist also über die Klasse und ihre Eigenschaft informiert.

Beispielsweise,

  1. Bedingung für das Funktionsargument ist, dass es immer> 0 (größer als Null) oder nicht null sein sollte.
  2. Die Eigenschaft minimum_account_balance einer Kontoklasse gibt an, dass sie nicht unter 100 fallen darf. Daher sollten alle öffentlichen Funktionen diese Bedingung berücksichtigen und eine Klasseninvariante sicherstellen.
  3. regelbasierte Abhängigkeit zwischen Variablen, dh der Wert einer Variablen hängt von einer anderen ab. Wenn sich also eine mit einer festen Regel ändert, muss sich auch die andere ändern. Diese Beziehung zwischen 2 Variablen muss erhalten bleiben. Ist dies nicht der Fall, wird die Invariante verletzt.

11

Dies sind Fakten, die für eine Instanzklasse zutreffen müssen. Wenn eine Klasse beispielsweise eine Eigenschaft X hat und die Invariante X sein kann, muss sie größer als 0 sein. Meines Wissens gibt es keine integrierte Methode zum Verwalten von Invarianten. Sie müssen Eigenschaften privat machen und sicherstellen, dass Ihre Getter und Setter die Invarianzeigenschaft erzwingen.

Es sind Anmerkungen verfügbar, mit denen Eigenschaften mithilfe von Reflexion und Interzeptoren überprüft werden können. http://docs.oracle.com/javaee/7/api/javax/validation/constraints/package-summary.html

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.