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.

23voto

Esko Luontola Punkte 71758

Der Artikel Wirksame Java-Ausnahmen erklärt sehr schön, wann ungeprüfte und wann geprüfte Ausnahmen verwendet werden sollten. Hier sind einige Zitate aus diesem Artikel, um die wichtigsten Punkte hervorzuheben:

Unvorhergesehenes: Eine erwartete Bedingung, die eine alternative Antwort von einer Methode verlangt, die in Bezug auf den beabsichtigten Zweck der Methode ausgedrückt werden kann. Der Aufrufer der Methode erwartet diese Art von Bedingungen und hat eine Strategie, um mit ihnen umzugehen.

Störung: Ein ungeplanter Zustand, der eine Methode daran hindert, ihren beabsichtigten Zweck zu erfüllen, und der nicht ohne Bezugnahme auf die interne Implementierung der Methode beschrieben werden kann.

(SO erlaubt keine Tabellen, daher sollten Sie vielleicht das Folgende aus der Originalseite ...)

Kontingente

  • Wird als solche betrachtet: Ein Teil des Musters
  • Es wird erwartet, dass dies geschieht: Regelmäßig, aber selten
  • Wen interessiert das schon: Der vorgelagerte Code, der die Methode aufruft
  • Beispiele: Alternative Rückführungsmodi
  • Beste Kartierung: Eine kontrollierte Ausnahme

Störung

  • Gilt als: Eine böse Überraschung
  • Es wird erwartet, dass dies geschieht: Niemals
  • Wen interessiert das schon: Die Menschen, die das Problem lösen müssen
  • Beispiele: Programmierfehler, Fehlfunktionen der Hardware, Konfigurationsfehler, fehlende Dateien, nicht verfügbare Server
  • Bestes Mapping: Eine ungeprüfte Ausnahme

23voto

Le Dude Punkte 537

Artima veröffentlichte ein Interview mit einem der Architekten von .NET, Anders Hejlsberg, der die Argumente gegen geprüfte Ausnahmen scharfsinnig behandelt. Ein kurzer Vorgeschmack:

Die throws-Klausel, zumindest die Art und Weise, wie sie in Java implementiert ist, zwingt Sie nicht unbedingt dazu, die Ausnahmen zu behandeln, aber wenn Sie sie nicht behandeln, zwingt sie Sie dazu, genau zu erkennen, welche Ausnahmen durchgehen könnten. Sie müssen entweder die deklarierten Ausnahmen abfangen oder sie in Ihre eigene throws-Klausel aufnehmen. Um diese Anforderung zu umgehen, tun die Leute lächerliche Dinge. Zum Beispiel schmücken sie jede Methode mit "throws Exception". Das macht die Funktion völlig zunichte, und Sie haben den Programmierer dazu gebracht, noch mehr Geschwafel zu schreiben. Damit ist niemandem geholfen.

20voto

David Lichteblau Punkte 3533

Kurz gesagt:

Ausnahmen sind eine Frage des API-Designs. -- Nicht mehr und nicht weniger.

Das Argument für kontrollierte Ausnahmen:

Um zu verstehen, warum geprüfte Ausnahmen nicht gut sind, drehen wir die Frage um und fragen: Wann oder warum sind geprüfte Ausnahmen attraktiv, d.h. warum sollte man wollen, dass der Compiler die Deklaration von Ausnahmen erzwingt?

Die Antwort liegt auf der Hand: Manchmal muss man 必要 um eine Ausnahme abzufangen, und das ist nur möglich, wenn der aufgerufene Code eine spezifische Ausnahmeklasse für den Fehler anbietet, an dem Sie interessiert sind.

Daher ist das Argument für geprüften Ausnahmen ist, dass der Compiler den Programmierer dazu zwingt, zu erklären, welche Ausnahmen ausgelöst werden, und hoffentlich wird der Programmierer dann auch spezifische Ausnahmeklassen und die Fehler, die sie verursachen, dokumentieren.

In der Realität wird jedoch nur allzu oft ein Paket com.acme wirft nur eine AcmeException und nicht bestimmte Unterklassen. Der Aufrufer muss dann Folgendes behandeln, deklarieren oder erneut signalisieren AcmeExceptions aber noch nicht sicher sein, ob ein AcmeFileNotFoundError passiert oder ein AcmePermissionDeniedError .

Wenn Sie sich also nur für eine AcmeFileNotFoundError Die Lösung besteht darin, eine Funktionsanforderung an die ACME-Programmierer zu richten und ihnen mitzuteilen, dass sie diese Unterklasse von implementieren, deklarieren und dokumentieren sollen. AcmeException .

Warum also die Mühe?

Daher kann der Compiler auch bei geprüften Ausnahmen den Programmierer nicht dazu zwingen, die nützlich Ausnahmen. Es ist nach wie vor nur eine Frage der Qualität der API.

Infolgedessen schneiden Sprachen ohne kontrollierte Ausnahmen in der Regel nicht viel schlechter ab. Programmierer könnten versucht sein, unspezifische Instanzen einer allgemeinen Error Klasse und nicht eine AcmeException aber wenn sie sich überhaupt um die Qualität ihrer API kümmern, werden sie lernen, eine AcmeFileNotFoundError schließlich.

Im Großen und Ganzen unterscheidet sich die Spezifikation und Dokumentation von Ausnahmen nicht wesentlich von der Spezifikation und Dokumentation von z.B. normalen Methoden. Auch diese sind eine Frage des API-Designs, und wenn ein Programmierer vergessen hat, eine nützliche Funktion zu implementieren oder zu exportieren, muss die API verbessert werden, damit man sinnvoll damit arbeiten kann.

Wenn Sie dieser Argumentation folgen, sollte es offensichtlich sein, dass die "Mühe" des Deklarierens, Abfangens und erneuten Werfens von Ausnahmen, die in Sprachen wie Java so üblich ist, oft wenig Wert hat.

Es ist auch erwähnenswert, dass die Java VM pas haben geprüfte Ausnahmen - nur der Java-Compiler prüft sie, und Klassendateien mit geänderten Ausnahme-Deklarationen sind zur Laufzeit kompatibel. Die Sicherheit der Java VM wird durch geprüfte Ausnahmen nicht verbessert, sondern nur der Codierungsstil.

19voto

Kategorien von Ausnahmen

Wenn ich über Ausnahmen spreche, beziehe ich mich immer auf Eric Lippert's Vexing Ausnahmen Blog-Artikel. Er teilt die Ausnahmen in diese Kategorien ein:

  • Tödlich - Diese Ausnahmen sind nicht deine Schuld Man kann sie nicht verhindern, und man kann nicht vernünftig mit ihnen umgehen. Zum Beispiel, OutOfMemoryError o ThreadAbortException .
  • Boneheaded - Diese Ausnahmen sind Ihre Schuld : Sie hätten sie verhindern müssen, und sie stellen Fehler in Ihrem Code dar. Zum Beispiel, ArrayIndexOutOfBoundsException , NullPointerException oder jede IllegalArgumentException .
  • Vexing - Diese Ausnahmen sind nicht außergewöhnlich Sie können sie nicht verhindern, aber Sie müssen mit ihnen fertig werden. Sie sind oft das Ergebnis einer unglücklichen Design-Entscheidung, wie z. B. das Werfen von NumberFormatException de Integer.parseInt anstelle der Bereitstellung eines Integer.tryParseInt Methode, die bei einem Parse-Fehler ein boolesches false zurückgibt.
  • Exogen - Diese Ausnahmen sind in der Regel außergewöhnlich nicht Ihre Schuld, Sie können sie (vernünftigerweise) nicht verhindern, aber Sie müssen mit ihnen umgehen . Zum Beispiel, FileNotFoundException .

Ein API-Benutzer:

  • darf nicht Griff tödlich o dummes Zeug Ausnahmen.
  • sollte Griff lästig Ausnahmen, aber sie sollten in einer idealen API nicht auftreten.
  • muss Griff exogenes Ausnahmen.

Geprüfte Ausnahmen

Die Tatsache, dass der API-Benutzer muss eine bestimmte Ausnahme zu behandeln, ist Teil des Methodenvertrags zwischen dem Aufrufer und dem Aufgerufenen. Der Vertrag legt unter anderem fest: die Anzahl und die Art der Argumente, die der Aufrufer erwartet, die Art des Rückgabewerts, den der Aufrufer erwarten kann, und die Ausnahmen, die der Aufrufer behandeln soll .

Desde lästig Ausnahmen sollten in einer API nicht existieren, nur diese exogenes Ausnahmen müssen sein geprüfte Ausnahmen Teil des Vertrags über die Methode zu sein. Relativ wenige Ausnahmen sind exogenes Daher sollte jede API relativ wenige geprüfte Ausnahmen haben.

Eine geprüfte Ausnahme ist eine Ausnahme, die muss behandelt werden . Die Behandlung einer Ausnahme kann so einfach sein, wie sie zu schlucken. So! Die Ausnahme wird behandelt. Punkt. Wenn der Entwickler sie so behandeln will, gut. Aber er kann die Ausnahme nicht ignorieren, und er wurde gewarnt.

API-Probleme

Aber jede API, die die lästig y tödlich Ausnahmen (z. B. die JCL) werden die API-Benutzer unnötig belasten. Solche Ausnahmen haben behandelt werden, aber entweder ist die Ausnahme so häufig, dass sie von vornherein keine Ausnahme hätte sein dürfen, oder es kann bei der Behandlung der Ausnahme nichts getan werden. Und este veranlasst Java-Entwickler, geprüfte Ausnahmen zu hassen.

Außerdem haben viele APIs keine richtige Ausnahmeklassenhierarchie, was dazu führt, dass alle Arten von nicht-exogenen Ausnahmeursachen durch eine einzige geprüfte Ausnahmeklasse dargestellt werden (z. B. IOException ). Und das führt auch dazu, dass Java-Entwickler geprüfte Ausnahmen hassen.

Schlussfolgerung

Exogen Ausnahmen sind solche, die Sie nicht verschuldet haben, die Sie nicht hätten verhindern können und die behandelt werden sollten. Diese bilden eine kleine Teilmenge aller Ausnahmen, die ausgelöst werden können. APIs sollten nur geprüft exogenes Ausnahmen und alle anderen Ausnahmen nicht angekreuzt. Dadurch werden bessere APIs geschaffen, die den API-Benutzer weniger belasten und somit die Notwendigkeit verringern, alle ungeprüften Ausnahmen abzufangen, zu schlucken oder erneut zu werfen.

Hassen Sie also nicht Java und seine geprüften Ausnahmen. Hassen Sie stattdessen die APIs, die geprüfte Ausnahmen übermäßig nutzen.

19voto

Mario Ortegón Punkte 18280

Ich habe in den letzten drei Jahren mit mehreren Entwicklern an relativ komplexen Anwendungen gearbeitet. Wir haben eine Codebasis, die häufig geprüfte Ausnahmen mit angemessener Fehlerbehandlung verwendet, und eine andere, bei der das nicht der Fall ist.

Bisher habe ich es einfacher gefunden, mit der Codebasis mit Checked Exceptions zu arbeiten. Wenn ich die API von jemand anderem verwende, ist es schön, dass ich genau sehen kann, welche Art von Fehlerbedingungen ich erwarten kann, wenn ich den Code aufrufe, und sie richtig behandeln kann, entweder durch Protokollierung, Anzeige oder Ignorieren (Ja, es gibt gültige Fälle für das Ignorieren von Ausnahmen, wie eine ClassLoader-Implementierung). Das gibt dem Code, den ich schreibe, die Möglichkeit, sich zu erholen. Alle Laufzeitausnahmen propagiere ich, bis sie zwischengespeichert und mit einem generischen Fehlerbehandlungscode behandelt werden. Wenn ich eine geprüfte Ausnahme finde, die ich nicht wirklich auf einer bestimmten Ebene behandeln möchte oder die ich für einen Programmierlogikfehler halte, dann verpacke ich sie in eine RuntimeException und lasse sie aufsteigen. Verschlucken Sie niemals eine Ausnahme ohne guten Grund (und gute Gründe dafür sind eher rar).

Wenn ich mit der Codebasis arbeite, die keine geprüften Ausnahmen hat, ist es für mich etwas schwieriger, vorher zu wissen, was ich erwarten kann, wenn ich die Funktion aufrufe, was einige Dinge furchtbar kaputt machen kann.

Dies ist natürlich alles eine Frage der Vorlieben und der Fähigkeiten der Entwickler. Beide Arten der Programmierung und Fehlerbehandlung können gleichermaßen effektiv (oder uneffektiv) sein, daher würde ich nicht sagen, dass es den einen Weg gibt.

Alles in allem finde ich es einfacher, mit Checked Exceptions zu arbeiten, besonders in großen Projekten mit vielen Entwicklern.

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