Eine ziemlich dumme Frage. Angesichts des Codes:
public static int sum(String a, String b) /* throws? WHAT? */ {
int x = Integer.parseInt(a); // throws NumberFormatException
int y = Integer.parseInt(b); // throws NumberFormatException
return x + y;
}
Können Sie sagen, ob es sich um gutes Java handelt oder nicht? Ich spreche von NumberFormatException
einer ungeprüften Ausnahme. Sie müssen es nicht als Teil der sum()
Signatur angeben . Soweit ich weiß, soll die Idee von ungeprüften Ausnahmen nur signalisieren, dass die Implementierung des Programms falsch ist, und noch mehr ist es eine schlechte Idee, ungeprüfte Ausnahmen abzufangen, da es so ist , als würde man ein schlechtes Programm zur Laufzeit reparieren .
Würde jemand bitte klären, ob:
- Ich sollte
NumberFormatException
als Teil der Signatur der Methode angeben . - Ich sollte meine eigene geprüfte Ausnahme (
BadDataException
) definieren,NumberFormatException
innerhalb der Methode behandeln und sie erneut als auslösenBadDataException
. - Ich sollte meine eigene aktivierte Ausnahme (
BadDataException
) definieren, beide Zeichenfolgen wie reguläre Ausdrücke validieren und meine auslösen,BadDataException
wenn sie nicht übereinstimmen. - Deine Idee?
Update :
Stellen Sie sich vor, es ist kein Open-Source-Framework, das Sie aus irgendeinem Grund verwenden sollten. Sie sehen sich die Signatur der Methode an und denken: "OK, sie wirft nie". Dann hast du eines Tages eine Ausnahme. Es ist normal?
Update 2 :
Es gibt einige Kommentare, die besagen, dass mein sum(String, String)
Design schlecht ist. Ich stimme absolut zu, aber für diejenigen, die glauben, dass das ursprüngliche Problem niemals auftauchen würde, wenn wir gutes Design hätten, ist hier eine zusätzliche Frage:
Die Problemdefinition lautet wie folgt: Sie haben eine Datenquelle, in der Zahlen als String
s gespeichert sind . Diese Quelle kann eine XML-Datei, eine Webseite oder ein Desktop-Fenster mit 2 Bearbeitungsfeldern sein.
Ihr Ziel ist es, die Logik zu implementieren, die diese 2 String
s benötigt, sie in int
s konvertiert und das Meldungsfeld "Die Summe ist xxx" anzeigt.
Unabhängig davon, mit welchem Ansatz Sie dies entwerfen / implementieren, haben Sie diese zwei Punkte innerer Funktionalität :
- Ein Ort , wo Sie konvertieren
String
zuint
- Ein Ort, an dem Sie 2
int
s hinzufügen
Die Hauptfrage meines ursprünglichen Beitrags lautet:
Integer.parseInt()
erwartet, dass die richtige Zeichenfolge übergeben wird. Wenn Sie eine fehlerhafte Zeichenfolge übergeben , bedeutet dies, dass Ihr Programm falsch ist (nicht " Ihr Benutzer ist ein Idiot"). Sie müssen den Code implementieren, in dem Sie einerseits Integer.parseInt () mit MUST-Semantik haben und andererseits mit den Fällen einverstanden sein müssen, in denen die Eingabe falsch ist - SOLLTE Semantik .
Also, kurz: wie kann ich implementieren SOLL Semantik , wenn ich nur haben MUST Bibliotheken .
Antworten:
Das ist eine gute Frage. Ich wünschte, mehr Menschen würden über solche Dinge nachdenken.
IMHO ist es akzeptabel, ungeprüfte Ausnahmen auszulösen, wenn Ihnen Müllparameter übergeben wurden.
Im Allgemeinen sollten Sie nicht werfen,
BadDataException
da Sie keine Ausnahmen verwenden sollten, um den Programmfluss zu steuern. Ausnahmen sind für das Außergewöhnliche. Aufrufer Ihrer Methode können wissen, bevor sie sie aufrufen, ob ihre Zeichenfolgen Zahlen sind oder nicht. Daher ist das Übergeben von Müll vermeidbar und kann daher als Programmierfehler betrachtet werden. Dies bedeutet, dass es in Ordnung ist, ungeprüfte Ausnahmen auszulösen.In Bezug auf die Deklaration ist
throws NumberFormatException
dies nicht so nützlich, da nur wenige dies bemerken, da die NumberFormatException deaktiviert ist. IDEs können jedoch davon Gebrauch machen und anbieten,try/catch
korrekt einzuwickeln . Eine gute Option ist die Verwendung von Javadoc, z.BEARBEITET :
Die Kommentatoren haben gültige Punkte gemacht. Sie müssen überlegen, wie dies verwendet wird und wie Ihre App insgesamt gestaltet ist.
Wenn die Methode überall verwendet wird und es wichtig ist, dass alle Aufrufer Probleme behandeln, deklarieren Sie die Methode als ausgelöste Ausnahme (was Aufrufer dazu zwingt, sich mit Problemen zu befassen), aber den Code mit
try/catch
Blöcken überladen .Wenn wir diese Methode andererseits mit Daten verwenden, denen wir vertrauen, deklarieren Sie sie wie oben, da nicht erwartet wird, dass sie jemals explodieren und Sie die Code-Unordnung von im Wesentlichen unnötigen
try/catch
Blöcken vermeiden .quelle
RuntimeException
behandelt werden soll? Sollte es der Anrufer sein oder sollte die Ausnahme schweben bleiben? Wenn ja, wie soll damit umgegangen werden?Exceptions
von niedrigeren Ebenen abzufangen und sie in Ausnahmen höherer Ebene einzuschließen, die für den Endbenutzer aussagekräftiger sind, und der zweite darin, einen nicht erfassten Ausnahmebehandler zu verwenden, der initialisiert werden kann der Anwendungsstarter.throws
Klausel ist eine nette Ergänzung, aber nicht erforderlich.Meiner Meinung nach wäre es vorzuziehen, die Ausnahmelogik so weit wie möglich zu behandeln. Daher würde ich die Unterschrift vorziehen
Mit Ihrer Methodensignatur würde ich nichts ändern. Entweder du bist
Daher wird die Ausnahmebehandlung in diesem Fall zu einem Dokumentationsproblem .
quelle
sum
Funktion, die zwei Zahlen erwartet, sollte zwei Zahlen erhalten. Die Art und Weise, wie Sie diese erhalten haben, hängt nicht mit dersum
Funktion zusammen. Trennen Sie Ihre Logik richtig . Also würde ich dies mit einem Designproblem in Verbindung bringen.Nummer 4. Wie angegeben, sollte diese Methode keine Zeichenfolgen als Parameter verwenden, sondern Ganzzahlen. In diesem Fall (da Java umbrochen wird, anstatt überzulaufen) besteht keine Möglichkeit einer Ausnahme.
ist viel klarer, was gemeint ist als
x = sum(a, b)
Sie möchten, dass die Ausnahme so nahe wie möglich an der Quelle (Eingabe) auftritt.
In Bezug auf die Optionen 1 bis 3 definieren Sie keine Ausnahme, da Sie von Ihren Anrufern erwarten, dass sie davon ausgehen, dass Ihr Code ansonsten nicht fehlschlagen kann. Sie definieren eine Ausnahme, um zu definieren, was unter bekannten Fehlerbedingungen geschieht, die für Ihre Methode eindeutig sind. Wenn Sie also eine Methode haben, die ein Wrapper um ein anderes Objekt ist und eine Ausnahme auslöst, geben Sie diese weiter. Nur wenn die Ausnahme für Ihre Methode eindeutig ist, sollten Sie eine benutzerdefinierte Ausnahme auslösen (frex, in Ihrem Beispiel, wenn sum nur positive Ergebnisse liefern sollte, wäre es angemessen, dies zu überprüfen und eine Ausnahme auszulösen, wenn andererseits Java Wenn Sie eine Überlaufausnahme auslösen, anstatt sie zu verpacken, geben Sie diese weiter, definieren sie nicht in Ihrer Signatur, benennen sie um oder essen sie.
Update als Antwort auf das Update der Frage:
Die Lösung hierfür besteht darin, die MUST-Bibliothek zu verpacken und einen SHOULD-Wert zurückzugeben. In diesem Fall eine Funktion, die eine Ganzzahl zurückgibt. Schreiben Sie eine Funktion, die eine Zeichenfolge verwendet und ein Integer-Objekt zurückgibt - entweder funktioniert es oder es gibt null zurück (wie Ints.tryParse von guava). Wenn Ihre Validierung von Ihrer Operation getrennt ist, sollte Ihre Operation ints dauern. Ob Ihre Operation mit Standardwerten aufgerufen wird, wenn Sie eine ungültige Eingabe haben, oder wenn Sie etwas anderes tun, hängt von Ihren Spezifikationen ab - das meiste, was ich dazu sagen kann, ist, dass es wirklich unwahrscheinlich ist, dass der Ort, an dem diese Entscheidung getroffen wird, in der Operation liegt Methode.
quelle
sum(int, int)
und habeparseIntFromString(String)
. Um die gleiche Funktionalität zu erhalten, muss ich in beiden Fällen einen Code schreiben, in dem 2 Aufrufe anparseIntFromString()
und 1 Aufruf an vorhanden sindsum()
. Betrachten Sie diesen Fall, wenn es für Sie sinnvoll ist - ich sehe keinen Unterschied vom Standpunkt des ursprünglichen Problems.int sum(String, String)
in der Realität niemals möglich ist?Ich glaube schon. Es ist eine schöne Dokumentation.
Manchmal ja. Die überprüften Ausnahmen werden in einigen Fällen als besser angesehen, aber die Arbeit mit ihnen ist eine ziemliche PITA. Aus diesem Grund verwenden viele Frameworks (z. B. Hibernate) nur Laufzeitausnahmen.
Noch nie. Mehr Arbeit, weniger Geschwindigkeit (es sei denn, Sie erwarten, dass das Auslösen der Ausnahme eine Regel ist) und überhaupt kein Gewinn.
Überhaupt keine.
quelle
Nr. 4.
Ich denke, ich würde die Methode überhaupt nicht ändern. Ich würde versuchen, die aufrufende Methode oder höher im Stack-Trace zu umgehen, wenn ich mich in einem Kontext befinde, in dem ich mich mit Geschäftslogik ordnungsgemäß von der Ausnahme erholen kann.
Ich würde # 3 nicht mit Sicherheit machen, da ich es für übertrieben halte.
quelle
Angenommen, das, was Sie schreiben, wird (wie eine API) von jemand anderem verwendet, dann sollten Sie mit 1 gehen. NumberFormatException dient speziell zum Zweck der Übermittlung solcher Ausnahmen und sollte verwendet werden.
quelle
Zuerst müssen Sie sich fragen, muss sich der Benutzer meiner Methode Sorgen um die Eingabe falscher Daten machen oder wird von ihm erwartet, dass er die richtigen Daten eingibt (in diesem Fall String). Diese Erwartung wird auch als Design by Contract bezeichnet .
und 3. Ja, Sie sollten wahrscheinlich BadDataException definieren oder noch besser einige der aufregenden wie NumberFormatException verwenden, sondern die Standardnachricht anzeigen lassen. Fangen Sie NumberFormatException in der Methode ab und werfen Sie sie erneut mit Ihrer Nachricht, ohne zu vergessen, dass der ursprüngliche Stack-Trace enthalten ist.
Es hängt von der Situation ab, aber ich würde wahrscheinlich NumberFormatException mit einigen zusätzlichen Informationen erneut auslösen. Und es muss auch eine Javadoc-Erklärung geben, wofür die erwarteten Werte sind
String a, String b
quelle
Hängt stark von dem Szenario ab, in dem Sie sich befinden.
Fall 1. Es sind immer Sie, die den Code debuggen, und niemand anderes und eine Ausnahme verursachen keine schlechte Benutzererfahrung
Wirf die Standard NumberFormatException
Fall 2: Code sollte äußerst wartbar und verständlich sein
Definieren Sie Ihre eigene Ausnahme und fügen Sie beim Auslösen viel mehr Daten zum Debuggen hinzu.
Sie brauchen keine Regex-Prüfungen, da dies bei schlechten Eingaben ohnehin zur Ausnahme führen wird.
Wenn es ein Code auf Produktionsebene wäre, wäre meine Idee, mehr als eine benutzerdefinierte Ausnahme zu definieren, wie z
und mit all diesen getrennt umgehen
quelle
Insgesamt ist eine NumberFormaException deaktiviert, da erwartet wird, dass korrekt analysierbare Eingaben bereitgestellt werden. Die Eingabevalidierung ist etwas, mit dem Sie umgehen sollten. Das eigentliche Parsen der Eingabe ist jedoch der einfachste Weg, dies zu tun. Sie können Ihre Methode einfach so lassen, wie sie ist, und in der Dokumentation warnen, dass eine korrekte Eingabe erwartet wird und jeder, der Ihre Funktion aufruft, beide Eingaben validieren sollte, bevor er sie verwendet.
quelle
Jedes außergewöhnliche Verhalten sollte in der Dokumentation geklärt werden. Entweder sollte angegeben werden, dass diese Methode einen speziellen Wert zurückgibt, falls ein Fehler auftritt (z. B.
null
durch Ändern des Rückgabetyps inInteger
), oder es sollte Fall 1 verwendet werden. Wenn der Benutzer es explizit in der Signatur der Methode hat, kann er es ignorieren, wenn er auf andere Weise für korrekte Zeichenfolgen sorgt. Es ist jedoch offensichtlich, dass die Methode diese Art von Fehler nicht selbst behandelt.quelle
Beantworten Sie Ihre aktualisierte Frage.
Ja, es ist völlig normal, "Überraschungs" -Ausnahmen zu bekommen. Denken Sie an alle Laufzeitfehler, die beim Programmieren aufgetreten sind.
Auch eine häufige Überraschungsausnahme von der für jede Schleife.
quelle
Obwohl ich der Antwort zustimme, dass die Laufzeitausnahme aus Sicht des Designs und der Benutzerfreundlichkeit durchgelassen werden darf, ist es eine gute Idee, sie in eine IllegalArgumentException zu packen, anstatt sie als NumberFormatException zu werfen. Dies macht dann den Vertrag Ihrer Methode klarer, in dem erklärt wird, dass ein rechtswidriges Argument an sie übergeben wurde, aufgrund dessen eine Ausnahme ausgelöst wurde.
In Bezug auf das Update der Frage "Stellen Sie sich vor, es ist kein Open-Source-Framework, das Sie aus irgendeinem Grund verwenden sollten. Sie sehen sich die Signatur der Methode an und denken:" OK, es wird nie ausgelöst ". Dann haben Sie eines Tages eine Ausnahme . Es ist normal?" Das Javadoc Ihrer Methode sollte immer das Verhalten Ihrer Methode (Vor- und Nacheinschränkungen) ausdrücken. Denken Sie an die Zeilen von say-Auflistungsschnittstellen, in denen, wenn eine Null nicht zulässig ist, der Javadoc sagt, dass eine Nullzeigerausnahme ausgelöst wird, obwohl sie niemals Teil der Methodensignatur ist.
quelle
Wenn Sie über gute Java-Praxis sprechen, ist es meiner Meinung nach immer besser
Um die ungeprüfte Ausnahme zu behandeln, analysieren Sie sie und verwenden Sie eine benutzerdefinierte ungeprüfte Ausnahme.
Während Sie eine benutzerdefinierte, nicht aktivierte Ausnahme auslösen, können Sie die Ausnahmemeldung hinzufügen, die Ihr Client verstehen kann, und auch die Stapelverfolgung der ursprünglichen Ausnahme drucken
Es ist nicht erforderlich, eine benutzerdefinierte Ausnahme als "Auslöser" zu deklarieren, da diese deaktiviert ist.
Auf diese Weise verletzen Sie nicht die Verwendung der nicht aktivierten Ausnahmen, für die gleichzeitig der Client des Codes den Grund und die Lösung für die Ausnahme leicht verstehen würde.
Auch eine ordnungsgemäße Dokumentation in Java-Doc ist eine gute Vorgehensweise und hilft sehr.
quelle
Ich denke, es hängt von Ihrem Zweck ab, aber ich würde es mindestens dokumentieren:
Oder nehmen Sie eine Seite aus dem Java-Quellcode für die Klasse java.lang.Integer:
quelle
Wie wäre es mit dem Eingabevalidierungsmuster, das von Googles 'Guava'-Bibliothek oder Apaches' Validator'-Bibliothek implementiert wurde ( Vergleich )?
Nach meiner Erfahrung wird es als bewährte Methode angesehen, die Parameter einer Funktion zu Beginn der Funktion zu validieren und gegebenenfalls Ausnahmen auszulösen.
Ich würde diese Frage auch als weitgehend sprachunabhängig betrachten. Die "gute Praxis" hier würde für alle Sprachen gelten, die Funktionen haben, die Parameter annehmen können, die möglicherweise gültig sind oder nicht.
quelle
Ich denke, Ihr allererster Satz von "Eine ziemlich dumme Frage" ist sehr relevant. Warum sollten Sie überhaupt eine Methode mit dieser Signatur schreiben? Ist es überhaupt sinnvoll, zwei Zeichenfolgen zu summieren? Wenn die aufrufende Methode zwei Zeichenfolgen summieren möchte, liegt es in der Verantwortung der aufrufenden Methode, sicherzustellen, dass sie gültige Ints sind, und sie vor dem Aufrufen der Methode zu konvertieren.
Wenn die aufrufende Methode in diesem Beispiel die beiden Zeichenfolgen nicht in eine int konvertieren kann, kann sie verschiedene Aktionen ausführen. Es hängt wirklich davon ab, auf welcher Ebene diese Summierung auftritt. Ich gehe davon aus, dass die String-Konvertierung dem Front-End-Code sehr nahe kommt (wenn sie ordnungsgemäß durchgeführt wurde), sodass Fall 1 am wahrscheinlichsten ist:
quelle
Auf diese Frage gibt es viele interessante Antworten. Aber ich möchte noch Folgendes hinzufügen:
Für das Parsen von Zeichenfolgen bevorzuge ich immer "reguläre Ausdrücke". Das Paket java.util.regex hilft uns dabei. Also werde ich mit so etwas enden, das niemals eine Ausnahme auslöst. Es liegt an mir, einen speziellen Wert zurückzugeben, wenn ich einen Fehler feststellen möchte:
Wie man sehen kann, ist der Code nur ein bisschen länger, aber wir können mit dem umgehen, was wir wollen (und Standardwerte für x und y festlegen, steuern, was mit else-Klauseln passiert usw.). Wir könnten sogar eine allgemeinere Transformation schreiben Routine, an die wir Strings übergeben können, Rückgabewerte defaut, REGEX-Code zum Kompilieren, Fehlermeldungen zum Auslösen, ...
Hoffe, es war nützlich.
Warnung: Ich konnte diesen Code nicht testen. Bitte entschuldigen Sie eventuelle Syntaxprobleme.
quelle
Integer.matcher
?private
Variable innerhalb der Methode? Fehlende()
für ifs, fehlt viel;
,x
,y
nicht erklärt,matcher
zweimal erklärt, ...Integer.matcher.group()
das soll seinInteger.parseInt(matcher.group())
)Sie sind mit diesem Problem konfrontiert, weil Benutzerfehler zu tief in den Kern der Anwendung eindringen und teilweise auch, weil Sie Java-Datentypen missbrauchen.
Sie sollten eine klarere Trennung zwischen Validierung von Benutzereingaben und Geschäftslogik haben, die richtige Datentypisierung verwenden und dieses Problem wird von selbst verschwinden.
Tatsache ist, dass die Semantik von
Integer.parseInt()
bekannt ist - es ist der Hauptzweck, gültige ganze Zahlen zu analysieren . Ihnen fehlt ein expliziter Schritt zur Validierung / Analyse von Benutzereingaben.quelle