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 y
Eigenschaft gibt und es ist nie null
und es hat einen Wert vom Typ Y
.
class Counter {
private int x;
public int count() { return x++; }
}
kann zwei wichtige Invarianten nicht beibehalten
- Dies
count
gibt aufgrund eines möglichen Unterlaufs niemals einen negativen Wert zurück.
- Das fordert
count
streng 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 count
immer normal erfolgreich zu sein (ohne TCB-Verstöße † ) , bleibt jedoch nicht erhalten, da count
mö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:
- Java-Klassen haben konsistent Eigenschaften und Methoden oder nicht, sodass Schnittstelleninvarianten einfach zu pflegen sind.
- Java-Klassen können ihre
private
Felder schützen , sodass Invarianten, die auf privaten Daten basieren, einfach zu pflegen sind.
- 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.
- In Java können sich
null
Werte auf vielfältige Weise einschleichen, sodass es schwierig ist, Invarianten mit "hat einen echten Wert" beizubehalten.
- 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.
- 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 private
Nachschlagetabellen 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.