webentwicklung-frage-antwort-db.com.de

Wann sollte eine IllegalArgumentException ausgelöst werden?

Ich mache mir Sorgen, dass dies eine Laufzeitausnahme ist, daher sollte sie wahrscheinlich sparsam verwendet werden.
Standard-Anwendungsfall:

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

Aber das scheint das folgende Design zu erzwingen:

public void computeScore() throws MyPackageException {
      try {
          setPercentage(userInputPercent);
      }
      catch(IllegalArgumentException exc){
           throw new MyPackageException(exc);
      }
 }

Um wieder eine geprüfte Ausnahme zu sein.

Okay, aber lass uns damit anfangen. Wenn Sie eine schlechte Eingabe machen, erhalten Sie einen Laufzeitfehler. Erstens ist das eine ziemlich schwierige Richtlinie, die einheitlich zu implementieren ist, da Sie möglicherweise die entgegengesetzte Konvertierung durchführen müssen:

public void scanEmail(String emailStr, InputStream mime) {
    try {
        EmailAddress parsedAddress = EmailUtil.parse(emailStr);
    }
    catch(ParseException exc){
        throw new IllegalArgumentException("bad email", exc);
    }
}

Und noch schlimmer - bei der Überprüfung von 0 <= pct && pct <= 100 kann davon ausgegangen werden, dass der Clientcode statisch ausgeführt wird. Dies gilt jedoch nicht für fortgeschrittenere Daten wie E-Mail-Adressen oder, was noch schlimmer ist, etwas, das mit einer Datenbank abgeglichen werden muss. Daher kann der Clientcode im Allgemeinen nicht vorbelegt werden -bestätigen.

Was ich damit sagen möchte, ist, dass ich keine sinnvolle konsistente Richtlinie für die Verwendung von IllegalArgumentException sehe. Es scheint, dass es nicht verwendet werden sollte und wir sollten unsere eigenen geprüften Ausnahmen einhalten. Was ist ein guter Anwendungsfall, um dies zu werfen?

85
djechlin

Behandeln Sie IllegalArgumentException als Vorbedingung und beachten Sie das Konstruktionsprinzip: Eine öffentliche Methode sollte ihre eigenen Vorbedingungen sowohl kennen als auch öffentlich dokumentieren.

Ich würde zustimmen, dass dieses Beispiel korrekt ist:

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

Wenn EmailUtil undurchsichtig ist , was bedeutet, dass die Voraussetzungen für den Endbenutzer nicht beschrieben werden können, dann ist eine geprüfte Ausnahme korrekt. Die zweite Version, korrigiert für dieses Design:

import com.someoneelse.EmailUtil;

public void scanEmail(String emailStr, InputStream mime) throws ParseException {
    EmailAddress parsedAddress = EmailUtil.parseAddress(emailStr);
}

Wenn EmailUtil transparent ist , zum Beispiel eine private Methode, deren Eigentümer die Klasse ist, ist IllegalArgumentException genau dann korrekt, wenn ihre Vorbedingungen in der Funktionsdokumentation beschrieben werden können. Dies ist auch eine korrekte Version: 

/** @param String email An email with an address in the form [email protected]
 * with no nested comments, periods or other nonsense.
 */
public String scanEmail(String email)
  if (!addressIsProperlyFormatted(email)) {
      throw new IllegalArgumentException("invalid address");
  }
  return parseEmail(emailAddr);
}
private String parseEmail(String emailS) {
  // Assumes email is valid
  boolean parsesJustFine = true;
  // Parse logic
  if (!parsesJustFine) {
    // As a private method it is an internal error if address is improperly
    // formatted. This is an internal error to the class implementation.
    throw new AssertError("Internal error");
  }
}

Dieses Design könnte in beide Richtungen gehen.

  • Wenn die Beschreibung von Vorbedingungen teuer ist oder die Klasse von Kunden verwendet werden soll, die nicht wissen, ob ihre E-Mails gültig sind, verwenden Sie ParseException. Die oberste Methode hier heißt scanEmail, die darauf hinweist, dass der Endbenutzer nicht untersuchte E-Mails senden möchte. Dies ist wahrscheinlich korrekt.
  • Wenn in der Funktionsdokumentation Vorbedingungen beschrieben werden können und die Klasse keine ungültige Eingabe beabsichtigt und daher ein Programmierfehler angezeigt wird, verwenden Sie IllegalArgumentException. Obwohl nicht "geprüft", wird die "Prüfung" zum Javadoc verschoben, in dem die Funktion dokumentiert wird, der der Client voraussichtlich befolgen soll.IllegalArgumentException Wenn der Client nicht erkennen kann, dass sein Argument zuvor illegal ist, ist es falsch.

Ein Hinweis zu IllegalStateException : Dies bedeutet "Der interne Status dieses Objekts (private Instanzvariablen) kann diese Aktion nicht ausführen." Der Endbenutzer kann den privaten Status nicht so locker sehen, dass er Vorrang vor IllegalArgumentException hat, wenn der Client-Aufruf nicht feststellen kann, dass der Status des Objekts inkonsistent ist. Ich habe keine gute Erklärung, wenn es gegenüber überzogenen Ausnahmen vorgezogen wird, obwohl Dinge wie zweimaliges Initialisieren oder Verlust einer Datenbankverbindung, die nicht wiederhergestellt wird, Beispiele sind.

0
djechlin

Das API-Dokument für IllegalArgumentException lautet:

Wird ausgelöst, um anzuzeigen, dass einer Methode ein ungültiges oder unangemessenes Argument übergeben wurde.

Aus dem Blick auf wie es in den jdk-Bibliotheken verwendet wird würde ich sagen:

  • Es scheint eine defensive Maßnahme zu sein, sich über offensichtlich schlechte Eingaben zu beklagen, bevor die Eingaben in die Werke eindringen können und mit einer unsinnigen Fehlermeldung zur Mitte gehen können.

  • Es wird für Fälle verwendet, in denen es zu lästig wäre, eine geprüfte Ausnahme auszulösen (obwohl dies im Code Java.lang.reflect erscheint, wo die Besorgnis über lächerliche Ebenen des Auscheckens von geprüftem Ausnahmefall nicht offensichtlich ist).

Ich würde IllegalArgumentException verwenden, um die letzten defensiven Argumentüberprüfungen auf allgemeine Dienstprogramme zu überprüfen (wobei versucht wird, konsistent mit der Verwendung von jdk zu bleiben), wobei erwartet wird, dass ein schlechtes Argument ein Programmierfehler ist, ähnlich einer NPE. Ich würde es nicht verwenden, um die Validierung im Geschäftscode zu implementieren. Ich würde es sicherlich nicht für das E-Mail-Beispiel verwenden.

Wenn Sie über "schlechte Eingabe" sprechen, sollten Sie überlegen, woher die Eingabe kommt.

Wenn die Eingabe von einem Benutzer oder einem anderen externen System eingegeben wird, auf das Sie keinen Einfluss haben, sollten Sie davon ausgehen, dass die Eingabe ungültig ist, und überprüfen Sie sie immer. Es ist in Ordnung, in diesem Fall eine geprüfte Ausnahme auszulösen. Ihre Anwendung sollte diese Ausnahme "wiederherstellen", indem sie dem Benutzer eine Fehlermeldung anzeigt.

Wenn die Eingabe von Ihrem eigenen System stammt, z. Ihre Datenbank oder einige andere Teile Ihrer Anwendung sollten Sie sich darauf verlassen können, dass sie gültig ist (sie sollte validiert worden sein, bevor sie dort ankam). In diesem Fall ist es absolut in Ordnung, eine ungeprüfte Ausnahme wie eine IllegalArgumentException auszulösen, die nicht abgefangen werden sollte (im Allgemeinen sollten Sie niemals ungeprüfte Ausnahmen einfangen). Es ist ein Fehler des Programmierers, dass der ungültige Wert überhaupt da war;) Sie müssen ihn korrigieren.

19
Tom

Laufzeitausnahmen "sparsam" zu werfen, ist nicht wirklich eine gute Richtlinie. - In effective Java wird die Verwendung von aktivierten Ausnahmen empfohlen, wenn der Aufrufer vernünftigerweise die Wiederherstellung von erwarten kann. (Ein Programmierfehler ist ein konkretes Beispiel: Wenn in einem bestimmten Fall ein Programmierfehler angezeigt wird, sollten Sie eine ungeprüfte Ausnahme auslösen; der Programmierer sollte einen Stack-Trace haben, wo das logische Problem aufgetreten ist, und nicht versuchen, es selbst zu behandeln.)

Wenn keine Hoffnung auf Genesung besteht, können Sie ungehinderte Ausnahmen verwenden. Es macht keinen Sinn, sie zu fangen, also ist das vollkommen in Ordnung.

Aus Ihrem Beispiel ist jedoch nicht zu 100% klar, welcher Fall dieses Beispiel in Ihrem Code enthält.

11
Louis Wasserman

Jede API sollte die Gültigkeit aller Parameter einer öffentlichen Methode überprüfen, bevor sie ausgeführt wird:

void setPercentage(int pct, AnObject object) {
    if( pct < 0 || pct > 100) {
        throw new IllegalArgumentException("pct has an invalid value");
    }
    if (object == null) {
        throw new IllegalArgumentException("object is null");
    }
}

Sie stellen 99,9% der Fehler in der Anwendung dar, da sie nach unmöglichen Vorgängen fragen. Am Ende handelt es sich dabei um Fehler, die die Anwendung zum Absturz bringen sollten (es handelt sich also um einen nicht wiederherstellbaren Fehler).

In diesem Fall sollten Sie die Anwendung beenden, um den Status der Anwendung nicht zu beschädigen.

Wie im offiziellen Oracle-Lernprogramm angegeben, heißt es:

Wenn von einem Client vernünftigerweise erwartet werden kann, dass er sich von einer Ausnahme erholt, dann machen Sie es zu einer geprüften Ausnahme. Wenn ein Client nichts zur Wiederherstellung tun kann Machen Sie aus der Ausnahme eine ungeprüfte Ausnahme.

Wenn ich eine Anwendung habe, die mit JDBC mit der Datenbank interagiert, und eine Methode, die das Argument als int item und double price verwendet. Das price für das entsprechende Element wird aus der Datenbanktabelle gelesen. Ich multipliziere einfach die Gesamtzahl von item, die mit dem price-Wert gekauft wurde, und gebe das Ergebnis zurück. Obwohl ich an meinem Ende (Anwendungsende) immer sicher bin, dass der Preisfeldwert in der Tabelle niemals negativ sein könnte. Aber was ist, wenn der Preiswert herauskommt negativ ? Es zeigt, dass es ein ernstes Problem mit der Datenbankseite gibt. Möglicherweise falsche Preiserfassung durch den Betreiber. Dies ist ein Problem, das der andere Teil der Anwendung, der diese Methode aufruft, nicht vorhersehen kann und sich nicht davon erholen kann. Es ist eine BUG in Ihrer Datenbank. Also, und IllegalArguementException() sollte in diesem Fall geworfen werden, was diesen the price can't be negative angeben würde. 
Ich hoffe, dass ich meinen Standpunkt klar ausgedrückt habe ..

4
Vishal K