2016-12-25 5 views
20

Blick durch Joshua Blochs 'Effective Java - Second Edition', stieß ich auf den folgenden Code auf Seite 152:Wird AssertionError aktiv in Java empfohlen?

double apply(double x, double y) { 
    switch(this) { 
     case PLUS: return x + y; 
     case MINUS: return x - y; 
     case TIMES: return x * y; 
     case DIVIDE: return x/y; 
    } 
    throw new AssertionError("Unknown op: " + this); 
} 

Jetzt verwirrt mich, was ist, dass die AssertionError aktiv geworfen wird. Wird das als gute Praxis angesehen? Zu meinem Verständnis werden Assertions verwendet, um den Code nicht zu interferieren, so dass das Verhalten sich nicht ändert, wenn die Java-Programmierung ohne aktivierte Assertionen gestartet wird und die assert-Anweisungen daher nicht ausgeführt werden. Ich wäre ziemlich verwirrt, wenn ich ein AssertionException bekommen würde, wenn ich ein Programm ausführen würde, ohne sogar Assertions aktiviert zu haben.

Obwohl ich verstehe, dass der Beispielfall ziemlich oft passieren kann, dass Sie ein paar verschiedene Optionen analysieren und wenn es keine von ihnen ist, sollten Sie eine Ausnahme auslösen.

Also ist es eine gute Übung, hier eine zu werfen, oder wäre es besser, eine andere zu werfen? Wenn ja, welche würde am besten passen? Vielleicht IllegalArgumentException?


Bearbeiten zur Klarstellung: Meine Frage ist, nicht, ob wir ein Error hier werfen sollte, aber wenn wir einen Exception oder eine Error werfen wollen, die man sollte es sein? Und ist es gute Praxis, AssertionError s aktiv zu werfen? Die Dokumentation sagt geworfen, um anzuzeigen, dass eine Behauptung gescheitert ist, so habe ich das Gefühl, dass wir es nicht aktiv werfen sollten. Ist das korrekt?


Zweite Edit: Klar Frage: Ist es sinnvoll, sich aktiv ein AssertionError zu werfen, oder dass vermieden werden sollte, auch wenn es möglich ist? (Meine Schätzung beim Lesen der Dokumente ist das letztere)

+1

ganz Meinung basierte Es ist. Für meinen Teil werfe ich 'AssertionError'. Ich denke, der Hauptgrund dafür, Behauptungen in der Produktion nicht zu überprüfen, sind die Kosten für die Überprüfung der Behauptung, und im Beispiel in der Frage glaube ich nicht, dass es eine gibt. Ich bin auf Blochs Seite. –

+1

Es ist definitiv ein Fehler, keine Ausnahme. Es gibt kein vernünftiges Szenario, in dem versucht werden sollte, den Fehler zu erkennen und die Ausführung fortzusetzen. –

+0

So, um die Titelfrage zu beantworten, von dem, was ich von der Diskussion hier verstehe: Ja, es ist in Ordnung, einen "AssertionError" in Ihrem Programm zu werfen. Und wenn Sie dies tun, müssen Assertions nicht eingeschaltet werden. –

Antwort

17

ich mit Herrn Bloch hier zustimmen würde - die Alternativen (IllegalArgumentException, IllegalStateException und UnsupportedOperationException) nicht richtig, die Schwere des Problems vermitteln, und Anrufer fälschlicherweise diesen Fall zu fangen und zu behandeln versuchen. In der Tat, wenn diese Linie jemals erreicht wird, ist das fragliche Programm gebrochen, und die einzige gesunde Sache zu tun ist, zu beenden.

Der Punkt hier ist, dass Enum eine endliche Menge von Werten hat, so dass es unmöglich sein sollte, die throw Linie zu erreichen - es würde nur auftreten, wenn die Enum-Definition geändert wurde, ohne auch diese Instanzmethode zu beheben. Das Werfen eines RuntimeException schlägt vor, dass der Anrufer einen Fehler machte, wenn tatsächlich die Methode (und die enum) selbst gebrochen ist. Das explizite Erhöhen eines AssertionError gibt korrekt an, dass die von dieser Methode erwarteten Invarianten verletzt wurden.

Guava hat einen hilfreichen Artikel, der when to raise different types of exceptions bricht. Sie schreiben:

A conventional assertion is a check that should only fail if the class itself (that contains the check) is broken in some way. (In some cases this can extend to the package.) These can take various forms including postconditions, class invariants, and internal preconditions (on non-public methods).

An impossible-condition check is one that cannot possibly fail unless surrounding code is later modified, or our deepest assumptions about platform behavior are grossly violated. These should be unnecessary but are often forced because the compiler can't recognize that a statement is unreachable, or because we know something about the control flow that the compiler cannot deduce.

Die Seite sagt ein AssertionError der empfohlene Weg ist, diese Fälle zu behandeln. Die Kommentare in ihrer Klasse Verify bieten auch einige nützliche Hinweise zum Auswählen von Ausnahmen. In Fällen, in denen AssertionError zu stark scheint, kann ein VerifyException ein guter Kompromiss sein.

In Bezug auf die spezifische Frage der Error oder RuntimeException, tut es nicht wirklich wichtig (beide sind nicht markiert und werden daher möglicherweise den Call-Stack reisen, ohne erwischt zu werden), aber Anrufer eher von einem RuntimeException zu erholen, um zu versuchen . Absturz der Anwendung in einem Fall wie diesem ist ein Feature, weil sonst wir eine Anwendung ausführen, die (an dieser Stelle) nachweislich falsch ist. Es ist sicherlich weniger wahrscheinlich, dass Anrufer AssertionError (oder Error oder Throwable) fangen und behandeln, aber natürlich können Anrufer tun, was sie wollen.

+0

Sie würden also sagen, dass es in Ordnung ist, in einigen Fällen aktiv einen 'AssertionError' zu werfen? Oder sollte das nur vom Compiler gemacht werden, wenn die Anweisung 'assert' verwendet wird? –

+0

Sicherlich; Ein 'AssertionError' zeigt an, dass die Invarianten eines Programms verletzt wurden. – dimo414

5

Meiner Meinung nach wäre ein AssertionError hier falsch zu verwenden.

From the docs, ein AssertionError erstreckt Basisklasse Error

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch.

Ein Fehler sollte tödlich sein, während ich Ihr Programm zu handhaben erwarten würde, und zeigt dem Benutzer eine Warnmeldung über den unbekannten Betrieb.

Wenn hier etwas, würde ich erwarten, dass ein UnsupportedOperationException geworfen wird, und anderswo im Call-Stack behandelt.

Thrown to indicate that the requested operation is not supported.

Betrachten wir den Fall, wo nicht in einem Rechner, aber keinen Code Fluss, der ENUMs verwendet:

Wenn ein Entwickler einen neuen Wert zu einem vorhandenen Enum hinzuzufügen wäre, würde ich nicht Funktionen erwarten, die machen Verwendung dieser vorhandenen Enumeration zum Aufrufen eines Fehlers, nur weil der neue Wert nicht unterstützt wird.

+0

Sicher ist es fraglich, ob wir eine Nachricht anzeigen oder einen Fehler ausgeben sollen. Nehmen wir an, wir wollen einen Fehler werfen: Welcher wäre der richtige? Kann es wirklich "AssertionError" sein? Die Dokumentation sagt auch _Thrown, um anzuzeigen, dass eine Assertion fehlgeschlagen ist_. Aber hier gab es überhaupt keine Behauptung - so habe ich das Gefühl, dass das gegen die Dokumentation ist. Recht? –

+0

Siehe meine Bearbeitung. Ich würde hier nicht unbedingt eine Behauptung verwenden wollen. Behauptungen sollten verwendet werden, um einen Fehler zu erhöhen, wenn etwas, von dem allgemein bekannt ist, nicht wahr ist. –

+0

Es ist nicht falsch, assertionerror in diesem Fall zu verwenden, da es behauptet, dass dieser Fall niemals erreicht wird. – GurV

1

Ich denke, sowohl AssertionError oder IllegalAE sind hier nicht sehr gut. Assertion Error ist nicht gut, wie in Matts Antwort angegeben. Und die Argumente sind hier nicht falsch, diese werden nur an eine falsche Methode übergeben. Also könnte IAE auch nicht gut sein. Natürlich ist dies auch eine meinungsbasierte Frage und Antwort.

Ich bin mir auch nicht sicher, ob die Aktivierung der Assertion zwingend ist, um AssertionError zu werfen, oder AssertionError bedeutet, dass Assertions aktiviert wurden.

0

Wie ich verstehe, ist Ihre Methode eine Methode des Enum-Objekts. In den meisten Fällen, wenn jemand einen neuen Enum-Wert hinzufügt, sollte er auch die "Anwenden" -Methode ändern. In diesem Fall sollten Sie UnsupportedOperationException auslösen.

4

In Bezug auf Fehler, die Java Tutorial Zustände:

The second kind of exception is the error. These are exceptional conditions that are external to the application, and that the application usually cannot anticipate or recover from.

Auch die Programming With Assertions Führungszustände:

Do not use assertions for argument checking in public methods.

Ich denke also Ausnahme ist der richtige Weg für diese Art von Fällen zu überprüfen.

Ich empfehle die Verwendung einer new UnsupportedOperationException("Operator " + name() + " is not supported.");, da es das Problem meiner Meinung nach besser beschreibt (d. H. Ein Entwickler einen Enum-Wert hinzugefügt, aber vergessen, den erforderlichen Fall zu implementieren).

Jedoch denke ich, dass diese Probe Fall ein AbstractEnum Entwurfsmuster anstelle eines Schalters verwendet werden soll:

da dieser Code
PLUS { 
    double apply(double x, double y) { 
     return x + y; 
    } 
}, 
MINUS { 
    double apply(double x, double y) { 
     return x - y; 
    } 
}, 
TIMES { 
    double apply(double x, double y) { 
     return x * y; 
    } 
}, 
DIVIDE { 
    double apply(double x, double y) { 
     return x/y; 
    } 
}; 

abstract double apply(double x, double y); 

Es ist weniger Fehler nicht kompilieren anfällig, bis jeder Fall apply implementiert.

+0

"* Verwenden Sie keine Assertionen für die Überprüfung von Argumenten in öffentlichen Methoden. *" In Blochs Beispiel wird das Argument einer öffentlichen Methode nicht überprüft - es wird behauptet, dass die * Klasse selbst * korrekt definiert ist. Ich würde vorschlagen, den zitierten Abschnitt von Effektivem Java zu lesen; Er fährt fort, das Muster, das du beschreibst, zu erforschen, und erklärt, warum es manchmal vorzuziehen ist, ein Enum zu verwenden. – dimo414

2

würde ich

bevorzugen
double apply(double x, double y) { 
    switch(this) { 
     case PLUS: return x + y; 
     case MINUS: return x - y; 
     case TIMES: return x * y; 
     default: assert this==DIVIDE: return x/y; 
    } 
} 
  1. Wir sollten nicht AssertionError werfen, weil es für die tatsächliche Behauptungen reserviert werden sollte.
  2. Anders als Assertions und einige catch-Blöcke sollte es kein einziges Bit Code geben, das nicht praktikabel erreicht werden kann.

Aber ich am liebsten würde https://stackoverflow.com/a/41324246/348975

Verwandte Themen