Ist es besser, assert oder IllegalArgumentException für die erforderlichen Methodenparameter zu verwenden?

87

Was ist in Java besonders zu empfehlen und warum? Beide Typen lösen Ausnahmen aus, daher ist der Umgang mit ihnen in dieser Hinsicht der gleiche. assertist etwas kürzer, aber ich bin mir nicht sicher, wie wichtig das ist.

public void doStuff(Object obj) {
    assert obj != null;
    ...
}

vs

public void doStuff(Object obj) {
    if (obj == null) {
        throw new IllegalArgumentException("object was null");
    }
    ...
}
Daenyth
quelle
Ich bevorzuge obj.hashCode()stattdessen eine einfache ;-)
Marco

Antworten:

117

IN ACHT NEHMEN!

Zusicherungen werden zur Laufzeit entfernt, sofern Sie beim Kompilieren des Codes nicht explizit angeben, dass Zusicherungen aktiviert werden sollen. Java-Assertions dürfen nicht im Produktionscode verwendet werden und sollten auf private Methoden beschränkt werden (siehe Ausnahme vs. Assertion ), da private Methoden nur den Entwicklern bekannt sein und von diesen verwendet werden sollen. Auch assertwerfen AssertionError , die sich Errornicht Exception, und die in der Regel bedeutet , dass Sie einen sehr abnormal Fehler haben (wie „OutOfMemoryError“ , die von schwer zu erholen, nicht wahr?) Sie sind nicht in der Lage sein zu behandeln , erwartet.

Entfernen Sie das Flag "enable assertions" und überprüfen Sie mit einem Debugger, ob Sie den Auslöser "IllegalArgumentException" nicht verwenden ... da dieser Code nicht kompiliert wurde (erneut, wenn "ea" entfernt wurde).

Es ist besser, die zweite Konstruktion für öffentliche / geschützte Methoden zu verwenden, und wenn Sie etwas wollen, das in einer Codezeile ausgeführt wird, gibt es mindestens einen Weg, den ich kenne. Ich persönlich verwende die Spring Framework - AssertKlasse, die einige Methoden zum Überprüfen von Argumenten enthält und bei Fehlern "IllegalArgumentException" auslöst. Grundsätzlich ist das, was Sie tun:

Assert.notNull(obj, "object was null");

... der tatsächlich genau den Code ausführt, den Sie in Ihrem zweiten Beispiel geschrieben haben. Es gibt ein paar andere nützliche Methoden wie hasText, hasLengthda drin.

Ich schreibe nicht gerne mehr Code als nötig, deshalb bin ich froh, wenn ich die Anzahl der geschriebenen Zeilen um 2 reduziere (2 Zeilen> 1 Zeile) :-)

Jalayn
quelle
Ah, ich habe vergessen, dass Behauptungen entfernt werden! Gute Antwort. Ich werde ein bisschen warten, um zu sehen, ob noch etwas hereinkommt, dann akzeptiere es :)
Daenyth
2
Beachten Sie, dass es kein Flag gibt, das Assertions zur Kompilierungszeit entfernt (obwohl sie möglicherweise durch bedingte Komplikation entfernt werden ). Zusicherungen sind zur Laufzeit standardmäßig deaktiviert (ich glaube, die JVM behandelt sie als NOOP), können jedoch über java -eaund programmgesteuert aktiviert werden . @ Jalayn Ich denke, Behauptungen im Produktionscode sind absolut gültig, da sie für das Debuggen im Feld
Justin Muller
@ Jalayn, -1. Der Compiler ist nicht entfernen Behauptung Code. Auch wenn sie nur ausgeführt werden, wenn Sie cmd ausführen java -ea.
Pacerier
5
Das Spring-Framework wird nicht benötigt, wenn Sie es verwenden könnenObjects.requreNonNull
kambunktiver
46

Sie müssen eine Ausnahme verwenden. Die Verwendung einer Behauptung wäre ein Missbrauch der Funktion.

Ungeprüfte Ausnahmen dienen dazu, Programmierfehler der Benutzer Ihrer Bibliothek zu erkennen, während Zusicherungen dazu dienen, Fehler in Ihrer eigenen Logik zu erkennen . Dies sind separate Themen, die nicht gemischt werden sollten.

Zum Beispiel eine Behauptung

assert myConnection.isConnected();

bedeutet "Ich weiß, dass jeder Codepfad, der zu dieser Behauptung führt, sicherstellt, dass myConnectioneine Verbindung besteht. Wenn der obige Code keine gültige Verbindung herstellen konnte, hätte er eine Ausnahme auslösen oder zurückkehren müssen, bevor dieser Punkt erreicht ist."

Auf der anderen Seite ein Scheck

if (!myConnection.isConnected()) {
    throw new IllegalArgumentException("connection is not established");
}

bedeutet "Das Aufrufen meiner Bibliothek ohne Verbindungsaufbau ist ein Programmierfehler".

dasblinkenlight
quelle
1
Diese Informationen sind wirklich hilfreich, aber ich akzeptiere die von Jalayn, da sie darauf hinweisen, wie ich möglicherweise einen Fehler mit der assert-Methode einführen könnte.
Daenyth
4
ausgezeichneter Punkt "Ungeprüfte Ausnahmen dienen dazu, Programmierfehler der Benutzer Ihrer Bibliothek zu erkennen, während Behauptungen dazu dienen, Fehler in Ihrer eigenen Logik zu erkennen. Dies sind separate Punkte, die nicht gemischt werden sollten."
Asim Ghaffar
Stimmt, aber dies setzt voraus, dass OP eine Bibliothek schreibt. Wenn ihr Code nur für den internen Gebrauch bestimmt ist, ist eine Zusicherung akzeptabel.
user949300
11

Wenn Sie eine Funktion schreiben, die keinen nullgültigen Parameterwert zulässt , sollten Sie die @NonnullAnmerkung zur Signatur hinzufügen und Objects.requireNonNullprüfen, ob das Argument ein ist, nullund ein auslösen, wenn dies der Fall NullPointerExceptionist. Die @NonnullAnmerkung dient der Dokumentation und gibt in einigen Fällen beim Kompilieren hilfreiche Warnungen aus. Es verhindert nicht, dass nulles zur Laufzeit übergeben wird.

void doStuff(@Nonnull Object obj) {
    Objects.requireNonNull(obj, "obj must not be null");

    // do stuff...
}

Update: Die @NonnullAnnotation ist nicht Bestandteil der Java-Standardbibliothek. Stattdessen gibt es zahlreiche konkurrierende Standards aus Bibliotheken von Drittanbietern (siehe Welche @NotNull-Java-Annotation soll ich verwenden? ). Das bedeutet nicht, dass es eine schlechte Idee ist, es zu benutzen, nur dass es nicht Standard ist.

vorsichtig
quelle
Vielen Dank für den Hinweis Objects.requireNonNull(), das war neu für mich. Wissen Sie, ob es irgendwo eine ähnliche Methode "requireTrue ()" oder "requireEqual ()" gibt? Nichts gegen Spring's Assert, aber nicht jeder nutzt das.
user949300
@ user949300 Objects.requireNonNull()dient zur Argumentüberprüfung. Wenn ein Argument truegleich oder gleich etwas sein muss, ist das Argument sinnlos. Für die Fehler anderen Fällen als illegal Argumente, sollten Sie throwein , Exceptiondie mehr beschreibt genau den Fehler. Es gibt auch JUnit, Assertaber das ist für Tests.
kambunctious
Ich dachte eher, sagen für eine Quadratwurzelfunktion, Objects.requireTrue(x >= 0.0);oder aus einem HashObjects.requireEquals(hash.length == 256)
user949300
Welches @Nonnull muss ich verwenden? javax.validation.constraints.NotNull?
Aguid
Ich würde die Eclipse JDT-Annotationen verwenden , weil sie von klugen Jungs gemacht werden :). Dokumentation: help.eclipse.org/neon/… - Sie können IntelliJ auch für die Verwendung konfigurieren.
koppor
2

Ich ziehe es immer vor, IllegalArgumentException über Behauptungen zu werfen.

Zusicherungen werden hauptsächlich in JUnit oder anderen Testwerkzeugen verwendet, um Testergebnisse zu überprüfen / zu bestätigen. Andere Entwickler könnten daher den falschen Eindruck erwecken, dass Ihre Methode eine Testmethode ist.

Es ist auch sinnvoll, IllegalArgumentException auszulösen, wenn einer Methode ein ungültiges oder unangemessenes Argument übergeben wurde . Dies steht im Einklang mit der Ausnahmebehandlungskonvention, die von Java-Entwicklern befolgt wird.

rai.skumar
quelle
5
Behauptungen gab es ungefähr 40 Jahre vor JUnit - fragen Sie einen C-Programmierer nach ASSERT-Makros.
JBRWilkinson
3
Bei dieser Frage geht es nicht um c; Es geht um Java. Also habe ich im Kontext von Java geantwortet.
rai.skumar
Verwechseln Sie assert (das reservierte Schlüsselwort) und Assert (die JUnit-Klasse) nicht. Beide werden verwendet, um eine Variable zu überprüfen. Darüber hinaus sind sie zwei sehr unterschiedliche Dinge und verhalten sich sehr unterschiedlich.
Newtopian
1

IMO das zweite ist etwas besser, weil es mehr Informationen bringt und weiter erweitert werden könnte (zB durch Erweiterung der Ausnahmeklasse), um noch informativer zu sein, außerdem verwendet es keinen negativen Vergleich, der einfacher zu verstehen ist.

0lukasz0
quelle
1

Ich verwende nicht viel aserts, aber einen gemeinsamen Ansatz mit Lombock @NonNull: https://projectlombok.org/features/NonNull

Lombok-Implementierung: lombok.NonNull importieren;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

Java-Version:

 import lombok.NonNull;

public class NonNullExample extends Something {
  private String name;

  public NonNullExample(@NonNull Person person) {
    super("Hello");
    if (person == null) {
      throw new NullPointerException("person");
    }
    this.name = person.getName();
  }
}

Lombok ist eine sehr schöne Bibliothek, die ich überall benutze

Kensai
quelle