532 Stimmen

Das Argument gegen kontrollierte Ausnahmen

Seit einigen Jahren ist es mir nicht gelungen, eine vernünftige Antwort auf die folgende Frage zu finden: Warum sind einige Entwickler so gegen geprüfte Ausnahmen? Ich habe zahlreiche Gespräche geführt, Dinge in Blogs gelesen, gelesen, was Bruce Eckel zu sagen hatte (die erste Person, die ich sah, die sich gegen sie aussprach).

Ich schreibe gerade einen neuen Code und achte sehr genau darauf, wie ich mit Ausnahmen umgehe. Ich versuche, den Standpunkt der "Wir mögen keine geprüften Ausnahmen"-Leute zu verstehen, aber ich kann ihn immer noch nicht nachvollziehen.

Jedes Gespräch, das ich führe, endet mit der gleichen Frage, die nicht beantwortet wird... lassen Sie mich das klarstellen:

Im Allgemeinen (von der Art, wie Java entwickelt wurde),

  • Error ist für Dinge gedacht, die man niemals einfangen sollte (VM hat eine Erdnussallergie und jemand hat ein Glas Erdnüsse auf ihn fallen lassen)
  • RuntimeException ist für Dinge, die der Programmierer falsch gemacht hat (Programmierer ist über das Ende eines Arrays hinausgegangen)
  • Exception (außer RuntimeException ) ist für Dinge gedacht, die sich der Kontrolle des Programmierers entziehen (die Festplatte füllt sich beim Schreiben in das Dateisystem, das Limit für Dateihandles für den Prozess wurde erreicht und Sie können keine weiteren Dateien öffnen).
  • Throwable ist einfach der übergeordnete Typ für alle Ausnahmetypen.

Ein häufiges Argument, das ich höre, ist, dass, wenn eine Ausnahme auftritt, der Entwickler nur das Programm beenden muss.

Ein weiteres Argument, das ich häufig höre, ist, dass geprüfte Ausnahmen das Refactoring von Code erschweren.

Zu dem Argument "alles, was ich tun werde, ist, das Programm zu beenden" sage ich, dass Sie, selbst wenn Sie es beenden, eine vernünftige Fehlermeldung anzeigen müssen. Wenn Sie sich mit der Behandlung von Fehlern begnügen, werden Ihre Benutzer nicht sehr erfreut sein, wenn das Programm ohne eine klare Angabe des Grundes beendet wird.

Für die Leute, die meinen, dass dies das Refactoring erschwert, bedeutet dies, dass nicht die richtige Abstraktionsebene gewählt wurde. Anstatt eine Methode zu deklarieren, die ein IOException El IOException sollte in eine Ausnahme umgewandelt werden, die dem Geschehen besser entspricht.

Ich habe kein Problem damit, Main mit catch(Exception) (oder in einigen Fällen catch(Throwable) um sicherzustellen, dass das Programm ordnungsgemäß beendet werden kann - aber ich fange immer die spezifischen Ausnahmen ab, die ich benötige. Auf diese Weise kann ich zumindest eine entsprechende Fehlermeldung ausgeben.

Die Frage, die nie beantwortet wird, ist diese:

Wenn Sie werfen RuntimeException Unterklassen anstelle von Exception Unterklassen, wie können Sie dann wissen, was Sie fangen sollen?

Wenn die Antwort "fangen" lautet Exception dann gehen Sie auch mit Programmierfehlern genauso um wie mit Systemausnahmen. Das halte ich für falsch.

Wenn Sie sich Throwable dann behandeln Sie Systemausnahmen und VM-Fehler (und Ähnliches) auf die gleiche Weise. Das halte ich für falsch.

Wenn die Antwort lautet, dass Sie nur die Ausnahmen abfangen, von denen Sie wissen, dass sie ausgelöst werden, wie können Sie dann wissen, welche Ausnahmen ausgelöst werden? Was passiert, wenn Programmierer X eine neue Ausnahme auslöst und vergessen hat, sie abzufangen? Das erscheint mir sehr gefährlich.

Ich würde sagen, dass ein Programm, das einen Stack-Trace anzeigt, falsch ist. Empfinden Leute, die keine geprüften Ausnahmen mögen, das nicht auch so?

Wenn Sie also keine geprüften Ausnahmen mögen, können Sie bitte erklären, warum nicht UND die Frage beantworten, die nicht beantwortet wird?

Ich bin nicht auf der Suche nach Ratschlägen, wann ich eines der beiden Modelle verwenden sollte, sondern nach folgenden Informationen warum Menschen erstrecken sich von RuntimeException weil sie sich nicht gerne von Exception und/oder warum sie eine Ausnahme abfangen und dann eine RuntimeException statt ihre Methode mit Überwürfen zu versehen. Ich möchte die Gründe für die Abneigung gegen geprüfte Ausnahmen verstehen.

6voto

Kurt Schelfthout Punkte 8711

Tatsächlich erhöhen geprüfte Ausnahmen einerseits die Robustheit und Korrektheit Ihres Programms (Sie sind gezwungen, Ihre Schnittstellen korrekt zu deklarieren - die Ausnahmen, die eine Methode auslöst, sind im Grunde ein spezieller Rückgabetyp). Andererseits stehen Sie vor dem Problem, dass Sie, da Ausnahmen "aufsteigen", sehr oft eine ganze Reihe von Methoden ändern müssen (alle Aufrufer und die Aufrufer der Aufrufer usw.), wenn Sie die Ausnahmen ändern, die eine Methode auslöst.

Geprüfte Ausnahmen in Java lösen das letztgenannte Problem nicht; C# und VB.NET schütten das Kind mit dem Bade aus.

Ein schöner Ansatz, der einen Mittelweg einschlägt, wird beschrieben in dieses OOPSLA 2005 Papier (oder die zugehöriger technischer Bericht .)

Kurz gesagt, es erlaubt Ihnen zu sagen: method g(x) throws like f(x) , was bedeutet, dass g alle Ausnahmen auslöst, die f auslöst. Voila, kontrollierte Ausnahmen ohne das Problem der kaskadierenden Änderungen.

Obwohl es sich um eine akademische Abhandlung handelt, möchte ich Sie ermutigen, sie (teilweise) zu lesen, da sie die Vor- und Nachteile von kontrollierten Ausnahmen gut erklärt.

5voto

Martin Punkte 2726

Wie bereits gesagt wurde, gibt es in Java-Bytecode keine geprüften Ausnahmen. Sie sind einfach ein Compiler-Mechanismus, nicht anders als andere Syntaxprüfungen. Ich sehe geprüfte Ausnahmen in etwa so, wie ich den Compiler sehe, der sich über eine redundante Bedingung beschwert: if(true) { a; } b; . Das ist hilfreich, aber vielleicht habe ich das mit Absicht gemacht, also ignoriere ich Ihre Warnungen.

Tatsache ist, dass Sie nicht in der Lage sind, jeden Programmierer dazu zu zwingen, "das Richtige zu tun", wenn Sie kontrollierte Ausnahmen durchsetzen, und alle anderen sind jetzt Kollateralschäden, die Sie nur wegen der von Ihnen aufgestellten Regel hassen.

Reparieren Sie die schlechten Programme da draußen! Versuchen Sie nicht, die Sprache so zu ändern, dass sie sie nicht zulässt! Für die meisten Leute bedeutet "etwas gegen eine Ausnahme tun" eigentlich nur, den Benutzer darüber zu informieren. Ich kann den Benutzer genauso gut über eine ungeprüfte Ausnahme informieren, also halten Sie Ihre geprüften Ausnahmeklassen von meiner API fern.

5voto

Dave Elton Punkte 113

Der Versuch, nur die unbeantwortete Frage zu beantworten:

Wenn Sie RuntimeException-Unterklassen anstelle von Exception-Unterklassen werfen, wie wissen Sie dann, was Sie fangen sollen?

Die Frage enthält eine fadenscheinige Argumentation, IMHO. Nur weil die API Ihnen mitteilt, was sie auslöst, heißt das nicht, dass Sie in allen Fällen auf dieselbe Weise damit umgehen. Anders ausgedrückt: Die Ausnahmen, die Sie abfangen müssen, hängen von dem Kontext ab, in dem Sie die Komponente verwenden, die die Ausnahme auslöst.

Zum Beispiel:

Wenn ich einen Verbindungstester für eine Datenbank schreibe oder etwas, um die Gültigkeit eines vom Benutzer eingegebenen XPath zu überprüfen, dann möchte ich wahrscheinlich alle geprüften und ungeprüften Ausnahmen, die durch die Operation ausgelöst werden, abfangen und melden.

Wenn ich jedoch eine Verarbeitungs-Engine schreibe, werde ich eine XPathException (geprüft) wahrscheinlich genauso behandeln wie eine NPE: Ich würde sie bis an den Anfang des Worker-Threads laufen lassen, den Rest des Stapels überspringen, das Problem protokollieren (oder es zur Diagnose an eine Support-Abteilung schicken) und dem Benutzer eine Rückmeldung geben, damit er sich an den Support wenden kann.

4voto

Joshua Punkte 37898

Mein Bericht auf c2.com ist gegenüber seiner ursprünglichen Form weitgehend unverändert geblieben: CheckedExceptionsAreIncompatibleWithVisitorPattern

Zusammengefasst:

Das Visitor Pattern und seine Verwandten sind eine Klasse von Schnittstellen, bei denen sowohl der indirekte Aufrufer als auch die Schnittstellenimplementierung von einer Ausnahme wissen, während die Schnittstelle und der direkte Aufrufer eine Bibliothek bilden, die dies nicht wissen kann.

Die Grundannahme von CheckedExceptions ist, dass alle deklarierten Ausnahmen von jedem Punkt, der eine Methode mit dieser Deklaration aufruft, ausgelöst werden können. Das VisitorPattern entlarvt diese Annahme als fehlerhaft.

Das Endergebnis von geprüften Ausnahmen in Fällen wie diesen ist eine Menge ansonsten nutzloser Code, der im Wesentlichen die Beschränkung des Compilers für geprüfte Ausnahmen zur Laufzeit aufhebt.

Was das eigentliche Problem betrifft:

Meine allgemeine Idee ist die Top-Level-Handler muss die Ausnahme zu interpretieren und eine entsprechende Fehlermeldung anzuzeigen. Ich sehe fast immer entweder IO-Ausnahmen, Kommunikationsausnahmen (aus irgendeinem Grund APIs unterscheiden), oder Aufgabe-fatale Fehler (Programm-Bugs oder schwerwiegende Problem auf Backing-Server), so sollte dies nicht zu schwer sein, wenn wir eine Stack-Trace für ein schweres Server-Problem zu ermöglichen.

4voto

Chuck Conway Punkte 16031

Anders spricht über die Fallstricke von geprüften Ausnahmen und warum er sie aus C# herausgelassen hat in Folge 97 von Software Engineering Radio.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X