11.2 Assertions
11.2 Assertions
Assertions (engl. Versicherung, Zusage) erlauben den Entwicklern für Testzwecke bestimmte Randbedingungen zu prüfen die immer erfüllt sein sollen.
Assertions geben dem Entwickler die Möglichkeit logische Bedingungen für die folgenden Fälle zu Programmieren:
- Interne Invarianten
- Invarianten im Kontrollfluß
- Vorbedingungen, Nachbedingungen, Klasseninvarianten
Durch die Implementierung dieser Invarianten kann der Entwickler die Qualität seiner Implementierung erhöhen, da Bedingungen geprüft werden können die nie verletzt sein sollen.
Der Unterschied zu Ausnahmen (Exceptions) besteht darin, dass Assertions immer gelten sollten und diese daher nicht im Normalfall kontrolliert werden müssen, da sie den Programmablauf nur unnötig verlangsamen würden. Assertions haben eine große Ähnlichkeit mit Ausnahmen sie dienen jedoch unterschiedlichen Zwecken:
Assertion | Ausnahme (Exception) | |
---|---|---|
Einsatzbereich | Nur wenn die Logik des Programm in den Augen des Entwicklers verletzt wird | Jederzeit da sie Teil der regulären Ablaufsteuerung sind |
Auswirkungen bei der normalen Programmausführung | Keine. Sie werden nicht geprüft! | Können immer Auftreten. |
Sichtbarkeit für Endanwender | Nie: Wenn das Programm nicht explizit mit entsprechenden Optionen (-ea) gestartet wurde | Nur wenn sie nicht abgefangen und behandelt werden |
Zielgruppe | Helfen dem Entwickler bei der Fehlersuche (auch beim Endanwender) |
Zusammenarbeit der Entwickler: Teil der regulären externen Spezifikation von Klassen Endanwender: Klassifikationschema für Fehlermeldungen bei Programmabbrüchen (Unbehandelte Ausnahmen) |
Theoretischer Hintergrund | Erlauben das Implementieren von Invarianten oder Vor- und Nachbedingungen von Schleifen und Routinen |
Elegantes Konstrukt zum Verlassen von Blöcken zur Behandlung von seltenen Ereignissen. Implementierter Code wird übersichtlicher da man nicht bei jeder Operation einzeln Sonderfäller prüfen muss |
Assertions bieten die folgenden Vorteile für den Entwickler:
- Der Entwickler kann Annahmen von denen er ausgeht als logische Ausdrücke implementieren und ist nicht auf Kommentare angewiesen.
- Die Konsistenzprüfungen werden im Normalfall nicht abgearbeitet und produzieren daher keinerlei Laufzeitkosten für den Anwender
- Sie geben dem Entwickler die Möglichkeit auch nach der Auslieferung seiner Anwendung zusätzliche Informationen durch Einschalten des Assertionchecking zu erhalten.
- Hintergrund: Bei C und C++ Anwendungen werden bei Kundenproblemen oft spezielle Programme mit extra Debuginformationen ausgeliefert. Dies ist bei Java nicht nötig
Notation
Einfache Variante einer Assertion:
assert Ausdruck1;
Der Ausdruck Audruck1 wird ausgewertet. Hat das Ergebnis den Wert true (wahr) so ist die Zusage wahr und das Programm weiter ausführt. Trifft die Zusage (Assertion) nicht zu wird das Programm mit einem AssertionError abgebrochen (falls es den entspechenden Optionen zum Checken der Assertions aufgerufen wurde).
Eine zweite Syntaxvariante ist:
assert Ausdruck1: Ausdruck2;
Hier fährt das Programm wie im Fall zuvor mit der Ausführung fort wenn Ausdruck1 wahr ist. Ist Ausdruck1 jedoch unwahr wird Ausdruck2 ausgewertet und der entsprechenden Instanz von AssertionError als Parameter mitgegeben und dann in der Fehlermeldung mit ausgegeben. Dieser Rückgabewert unterstützt den Entwickler bei der Analyse des aufgetretenen Fehlerfalls.
Einschalten der Prüfungen im Laufzeitsystem
Das Prüfen von Assertions kann beim Starten einer Javaanwendung mit den Optionen -ea bzw. -enableassertions eingeschaltet werden oder mit der Option -da bzw. -disableassertions ausgeschaltet werden. Die Option wird vor dem Klassennamen dessen main() Methode gestartet werden soll angegeben:
java -ea Klassenname1 java -ea paketname1... Klassenname1 java -ea paketname1.Klassename2 Klassenname1 java -da Klassenname1 java -da paketname1... Klassenname1 java -da paketname1.Klassename2 Klassenname1java
Wichtig: Die Notation mit den drei Punkten paketname1... ist teil der Aufrufsyntax. Mit ihr werden die Assertions für alle Klassen in einem Paket angeschaltet.
Anwendungsbeispiele
Überprüfen korrekter Wertebereiche
Prüfen des Personenalters bei Rentenberechnungen
assert ((personenAlter>0) && (personenAlter<150)); assert (rentenEintrittsAlter>0);
Die gleichen Assertions in der Variante mit einer Ausgabe für die Konsole
assert ((personenAlter>0) && (personenAlter<150)): personenAlter; assert (rentenEintrittsAlter>0): "negatives Renteneintrittsalter "+ rentenEintrittsAlter;
Das Kontrollieren des Werts eines Monats:
int monat; ... switch (monat) { case 1: case 2: case 3: System.out.println("Q1"); break; case 4: case 5: case 6: System.out.println("Q2"); break; case 7: case 8: case 9: System.out.println("Q3"); break; case 10: case 11: case 12: System.out.println("Q4"); break; default: assert false; }
Prüfen eines Kontrollflusses
In einen Programm soll eine der Bedingungen Ausdruck1 oder Ausdruck2 immer erfüllt sein.
void testMethode() { for (int k = 0; k<= 99; k++) { if (k == 50) return; } assert false; }
Die Assertion kann Prüfen ob ein Fehlerfall vorliegt.
Geschichtlicher Hintergrund
Assertions wurden in Java durch JSR 42 (A Simple Assertion Facility) in JDK 1.4 eingeführt. Dies führt zu einem gewissen Kompatiblitätsproblem:
- Quellcode der das Schlüsselwort assert als normalen Bezeichner in JDK 1.3 verwendete wird in JDK 1.4 nicht übersetzen da das Schlüsselwort nicht als Namen akzeptiert wird
- Quellcode der für JDK 1.4 geschrieben wurde wird nicht unter JDK 1.3 übersetzen, da javac in JDK 1.3 nicht der Syntax von assertions umgehen kann.
- 6315 views
11.2.1 Übungen (Assertions)
11.2.1 Übungen (Assertions)Übung 1: Einfügen von Assertions
Nutzen Sie das Beispiel aus dem Abschnitt zu Schnittstellen.
Modifizieren Sie die Klasse Euro so, daß
- Beim Setzen des Centbetrags eine Assertion geworfen wird falls der Centbetrag nicht im korrekten Wertebereich ist. Nutzen Sie die erweiterte Syntax um eine vernünftige Fehlermeldung auszugeben.
- Melden Sie beim Multiplizieren eines Eurobetrags einen Faktor von Null (0) mit Hilfe einer Assertion. Nutzen Sie die erweiterte Syntax um eine Fehlermeldung auszugeben.
Starten Sie das Programm mit der Klasse TestEuro: Es sollte wie zuvor funktionieren
Starten Sie die Klasse TestEuro so, daß Assertions beachtet werden.
- Welche Option muß man beim Programmstart einfügen?
- Modifizieren Sie das Testprogramm so, dass die Assertion für die Multiplikation ausgelöst wird. Der Fall einer Multiplikation mit Null wird im aktuellen Testprogramm nicht getestet.
- 3089 views
11.2.2 Lösungen (Assertions)
11.2.2 Lösungen (Assertions)Übung 1: Einfügen von Assertions
Klasse Euro
package s1.block11; import static java.lang.Math.abs; import static java.lang.Math.signum; /** * * @author s@scalingbits.com * @version 1.1 */ public class Euro extends Number implements Waehrung, Comparable{ /** * Der gesamte Betrag wird intern in Cents verwaltet */ public final long cents; public Euro(long euros, long cents) { assert ((cents>=0) && (cents < 101)): "Cents Bereichsverletzung"; // Ignoriere Centsbetrag wenn er nicht im richtigen Intervall ist if ((cents<0) || (cents>=100)) cents=0; this.cents = (abs(euros)*100+cents) *(long)signum(euros); } @Override public int intValue() { return (int)cents/100; } @Override public long longValue() { return cents/100; } @Override public float floatValue() { // Signum und Absolutwert sind notwendig // da -2.20= -(2 + 0.2) sind. Falsch: -2 + 0.2 ergibt -1.8! return (float)cents/100f; } @Override public double doubleValue() { // Signum und Absolutwert sind notwendig // da -2.20= -(2 + 0.2) sind. Falsch: -2 + 0.2 ergibt -1.8! return (double)cents/100d; } @Override public String symbol() { return "€"; } @Override public String toString() { // Füge eine Null bei Centbeträgen zwischen 0 und 9 eine String leerstelle = ((abs(cents)%100)<10) ? "0" : ""; return Long.toString(cents/100L) + "." + leerstelle + Long.toString(abs(cents%100L)) + symbol(); } @Override public Waehrung mult(double d) { assert (d!=0): "Multplikation mit " + d + "nicht erlaubt"; long temp; temp = (long)((double)cents *d); return new Euro(temp/100L,abs(temp%100L)); } @Override public int compareTo(Object o) { int result; if (o instanceof Euro) { Euro e = (Euro) o; result = (int)(this.cents-e.cents); } else {result = -1;} // Alles was kein Euro ist, ist kleiner return result; } }
- 3021 views
11.3.3 Lernziele (Assertions)
11.3.3 Lernziele (Assertions)
Am Ende dieses Blocks können Sie:
|
Lernzielkontrolle
Sie sind in der Lage die folgenden Fragen zu beantworten: Fragen zu Annahmen (Assertions))
- 3159 views