web-dev-qa-db-de.com

Java-Konstruktorstil: Prüfparameter sind nicht null

Was sind die bewährten Methoden, wenn Sie über eine Klasse verfügen, die einige Parameter akzeptiert, aber keine davon null sein darf?

Folgendes ist offensichtlich, aber die Ausnahme ist etwas unspezifisch:

public class SomeClass
{
     public SomeClass(Object one, Object two)
     {
        if (one == null || two == null)
        {
            throw new IllegalArgumentException("Parameters can't be null");
        }
        //...
     }
}

Hier geben die Ausnahmen an, welcher Parameter null ist, aber der Konstruktor ist jetzt ziemlich hässlich:

public class SomeClass
{
     public SomeClass(Object one, Object two)
     {
        if (one == null)
        {
            throw new IllegalArgumentException("one can't be null");
        }           
        if (two == null)
        {
            throw new IllegalArgumentException("two can't be null");
        }
        //...
  }

Hier ist der Konstruktor übersichtlicher, aber jetzt ist der Konstruktorcode nicht wirklich im Konstruktor:

public class SomeClass
{
     public SomeClass(Object one, Object two)
     {
        setOne(one);
        setTwo(two);
     }


     public void setOne(Object one)
     {
        if (one == null)
        {
            throw new IllegalArgumentException("one can't be null");
        }           
        //...
     }

     public void setTwo(Object two)
     {
        if (two == null)
        {
            throw new IllegalArgumentException("two can't be null");
        }
        //...
     }
  }

Welcher dieser Stile ist am besten? 

Oder gibt es eine Alternative, die allgemein akzeptiert wird?

61
user130076

Der zweite oder der dritte. 

Weil es dem Benutzer Ihrer API mitteilt, was genau schiefgegangen ist.

Für weniger Ausführlichkeit verwenden Sie Validate.notNull(obj, message) von commons-lang. So sieht Ihr Konstruktor aus:

public SomeClass(Object one, Object two) {
    Validate.notNull(one, "one can't be null");
    Validate.notNull(two, "two can't be null");
    ...
}

Das Setzen des Schecks im Setter ist ebenfalls zulässig, mit demselben Ausführlichkeitskommentar. Wenn Ihre Setter auch die Objektkonsistenz beibehalten möchten, können Sie auch den dritten auswählen.

84
Bozho

Sie können eine der zahlreichen Bibliotheken verwenden, die für die Überprüfung der Vorbedingungen entwickelt wurden. Viele Codes in Google Guava verwenden com.google.common.base.Preconditions .

Einfache statische Methoden, die zu Beginn Ihrer eigenen Methoden aufgerufen werden, um die korrekten Argumente und den Status zu überprüfen. Dies erlaubt Konstrukte wie

 if (count <= 0) {
   throw new IllegalArgumentException("must be positive: " + count);
 }

durch das kompaktere ersetzt werden

 checkArgument(count > 0, "must be positive: %s", count);

Es hat checkNotNull , das in Guava ausgiebig verwendet wird. Sie können dann schreiben:

 import static com.google.common.base.Preconditions.checkNotNull;
 //...

 public SomeClass(Object one, Object two) {
     this.one = checkNotNull(one);
     this.two = checkNotNull(two, "two can't be null!");
     //...
 }

Die meisten Methoden sind überlastet, um entweder keine Fehlermeldung, eine feste Fehlermeldung oder eine templatisierte Fehlermeldung mit varargs zu akzeptieren.


Bei IllegalArgumentException vs. NullPointerException

Während Ihr ursprünglicher Code IllegalArgumentException auf null-Argumente wirft, wirft Guavas Preconditions.checkNotNull stattdessen NullPointerException.

Hier ein Zitat aus Effektive Java 2nd Edition: Punkt 60: Bevorzugung der Verwendung von Standardausnahmen:

Alle irrtümlichen Methodenaufrufe laufen wahrscheinlich auf ein illegales Argument oder einen illegalen Staat hinaus, aber andere Ausnahmen werden standardmäßig für bestimmte Arten eines illegalen Arguments und von Staaten verwendet. Wenn ein Aufrufer null in einem Parameter übergibt, für den Nullwerte verboten sind, schreibt die Konvention vor, dass NullPointerException anstelle von IllegalArgumentException ausgegeben wird.

Eine NullPointerException ist nicht nur für den Zugriff auf Mitglieder einer null-Referenz reserviert. Es ist ziemlich normal, sie zu werfen, wenn ein Argument null ist, wenn dies ein ungültiger Wert ist.

System.out.println("some string".split(null));
// throws NullPointerException
36

Alte Frage; eine weitere neue Antwort (bereits in einem anderen Kommentar erwähnt; aber ich denke, es lohnt sich eine eigene Antwort).

Java 7 fügte Java.lang.Objects.requireNonNull() zu den APIs hinzu, die jeder verwenden kann. Das Überprüfen aller Argumente für null führt zu einer kurzen Liste wie:

this.arg1 = Objects.requireNonNull(arg1, "arg1 must not be null");
this.arg2 = Objects.requireNonNull(arg2, "arg2 must not be null");

Side Notes:

  • stellen Sie sicher, dass Sie die beiden Argumente nicht umkehren. Das second-Argument ist die Nachricht, die für die NPE verwendet wird, die ausgelöst wird, wenn das Argument first den Wert null hat (wenn Sie sie umkehren wird niemals scheitern)
  • eine weitere bewährte Methode: Wenn möglich, machen Sie alle Ihre Klassenmitglieder endgültig (damit Sie sicher sein können: Wenn ein Objekt erfolgreich erstellt wurde, sind alle seine Mitglieder nicht null;
29
GhostCat

Ich hätte eine Gebrauchsmethode:

 public static <T> T checkNull(String message, T object) {
     if(object == null) {
       throw new NullPointerException(message);
     }
     return object;
  }

Ich würde es das Objekt zurückgeben lassen, damit Sie es in Zuweisungen wie folgt verwenden können:

 public Constructor(Object param) {
     this.param = checkNull("Param not allowed to be null", param);
 }

BEARBEITEN: Was die Vorschläge zur Verwendung einer Bibliothek von Drittanbietern betrifft, übertrifft insbesondere Google Vorbedingungen die oben genannten Punkte noch besser als mein Code. Wenn dies jedoch der einzige Grund ist, die Bibliothek in Ihr Projekt aufzunehmen, wäre ich zögerlich. Die Methode ist zu einfach.

4
Yishai

Abgesehen von den obigen Antworten, die alle gültig und vernünftig sind, denke ich, dass es gut ist, darauf hinzuweisen, dass das Prüfen auf Null vielleicht nicht unbedingt "gute Praxis" ist. (Unter der Annahme, dass andere Leser als das OP die Frage als dogmatisch ansehen)

Aus dem Misko-Hevery-Blog zur Testbarkeit: Zum Bestätigen oder Nicht-Bestätigen

3
qnoid

Vergleich der Möglichkeiten zur Überprüfung der Vorbedingungen in Java - Guava vs. Apache Commons vs. Spring Framework vs.

public static void fooSpringFrameworkAssert(String name, int start, int end) {
        // Preconditions
        Assert.notNull(name, "Name must not be null");
        Assert.isTrue(start < end, "Start (" + start + ") must be smaller than end (" + end + ")");

        // Do something here ...
    }

    public static void fooApacheCommonsValidate(String name, int start, int end) {
        // Preconditions
        Validate.notNull(name, "Name must not be null");
        Validate.isTrue(start < end, "Start (%s) must be smaller than end (%s)", start, end);

        // Do something here ...
    }

    public static void fooGuavaPreconditions(String name, int start, int end) {
        // Preconditions
        Preconditions.checkNotNull(name, "Name must not be null");
        Preconditions.checkArgument(start < end, "Start (%s) must be smaller than end (%s)", start, end);

        // Do something here ...
    }

    public static void fooPlainJavaAsserts(String name, int start, int end) {
        // Preconditions
        assert null != name : "Name must not be null";
        assert start < end : "Start (" + start + ") must be smaller than end (" + end + ")";

        // Do something here ...
    }

dies ist eine Zusammenfassung dieses Artikels: http://www.sw-engineering-candies.com/blog-1/comparison-of-ways-to-check-preconditions-in-Java

2
nekperu15739

Eine Alternative zum Auslösen einer ungeprüften Ausnahme wäre die Verwendung von assert. Andernfalls würde ich geprüfte Ausnahmen werfen, um den Aufrufer darauf aufmerksam zu machen, dass der Konstruktor nicht mit ungültigen Werten arbeitet.

Der Unterschied zwischen Ihren ersten beiden Lösungen - benötigen Sie eine detaillierte Fehlermeldung, müssen Sie wissen, welcher Parameter fehlgeschlagen ist, oder reicht es aus zu wissen, dass die Instanz aufgrund illegaler Argumente nicht erstellt werden konnte?

Beachten Sie, dass das zweite und das dritte Beispiel nicht korrekt berichten können, dass beide Parameter null waren.

Übrigens - ich stimme für eine Variante von (1):

if (one == null || two == null) {
    throw new IllegalArgumentException(
      String.format("Parameters can't be null: one=%s, two=%s", one, two));
}
1
Andreas_D

Anmerkungen für die statische Analyse sind ebenfalls nützlich, entweder zu den Laufzeitüberprüfungen hin oder an deren Stelle.

FindBugs stellt beispielsweise eine @NonNull-Annotation bereit. 

public SomeClass (@NonNull-Objekt eins, @NonNull-Objekt zwei) {

1
Andy Thomas

Sie können einfach eine Methode haben, die alle Konstruktorargumente übernimmt, die Sie überprüfen müssen. Diese Methode löst eine Ausnahme mit einer bestimmten Nachricht aus, abhängig davon, welches Argument ungültig ist .. Ihr Konstruktor ruft diese Methode auf, und wenn er übergeben wird, werden die Werte initialisiert.

0
Marc

Ich gehe davon aus, dass Sie über die eingebaute assert in Java sprechen. Meiner Meinung nach ist es keine wirklich gute Idee, es zu benutzen. Da es mit Kommandozeilenparametern ein- und ausgeschaltet werden kann. Deshalb sagen einige, dass es nur in privaten Methoden zulässig ist.

Meine Mentoren fordern mich auf, das Rad nicht neu zu erfinden! Ihr Rat ist, Bibliotheken zu verwenden. Sie sind (wahrscheinlich) gut gestaltet und getestet. Natürlich liegt es in Ihrer Verantwortung, dass Sie eine qualitativ hochwertige Bibliothek erwerben.

Andere sagen mir, dass Enterprise-Mitarbeiter - in einigen Begriffen - falsch sind, und Sie führen mehr Abhängigkeit ein - für einfache Aufgaben - als erforderlich. Ich kann diesen Punkt auch akzeptieren ... Aber hier ist meine jüngste Erfahrung:

Zuerst habe ich meine eigene private Methode geschrieben, um Nullparameter zu überprüfen. Es ist langweilig und überflüssig. Ich weiß, ich sollte es in eine Utility-Klasse einordnen. Aber warum sollte ich es zuerst schreiben, wenn jemand es schon getan hat? Ich kann Zeit sparen, wenn ich Unit-Tests nicht schreibe und ein vorhandenes Zeug entwickle. Wenn Sie nicht trainieren oder lernen möchten, würde ich dies nicht empfehlen.

Ich habe vor kurzem angefangen, die Guava von Google zu verwenden, und ich finde, zusammen mit Apache Commons - wenn Sie sie einmal verwendet haben, werden Sie nicht nur für diese eine Methode verwenden. Sie werden es immer mehr entdecken und nutzen. Am Ende wird Ihr Code dadurch kürzer, lesbarer, konsistenter und wartbarer.

Übrigens: Abhängig von Ihren Zielen würde ich mit 2 oder 3 eine der oben genannten Bibliotheken verwenden ...

0
uthomas