Dies klingt für mich nach einem einigermaßen häufigen Problem, mit dem Nachwuchsentwickler irgendwann konfrontiert sind: Sie kennen die Verträge, an denen sie teilnehmen, entweder nicht oder vertrauen ihnen nicht und überprüfen sie defensiv auf Nullen. Darüber hinaus verlassen sie sich beim Schreiben ihres eigenen Codes in der Regel darauf, Nullen zurückzugeben, um etwas anzuzeigen, sodass der Aufrufer nach Nullen suchen muss.
Anders ausgedrückt, es gibt zwei Fälle, in denen die Nullprüfung auftritt:
Wenn null eine gültige vertragliche Antwort ist; und
Wo es keine gültige Antwort ist.
(2) ist einfach. Verwenden Sie entweder assert
Anweisungen (Zusicherungen) oder lassen Sie Fehler zu (z. B. NullPointerException ). Assertions sind eine stark unterausgenutzte Java-Funktion, die in 1.4 hinzugefügt wurde. Die Syntax lautet:
assert <condition>
oder
assert <condition> : <object>
Dabei <condition>
handelt es sich um einen booleschen Ausdruck und <object>
um ein Objekt, dessen toString()
Methodenausgabe in den Fehler einbezogen wird.
Eine assert
Anweisung löst ein Error
( AssertionError
) aus, wenn die Bedingung nicht erfüllt ist. Standardmäßig ignoriert Java Zusicherungen. Sie können Zusicherungen aktivieren, indem Sie die Option -ea
an die JVM übergeben. Sie können Zusicherungen für einzelne Klassen und Pakete aktivieren und deaktivieren. Dies bedeutet, dass Sie Code mit den Zusicherungen während der Entwicklung und des Testens validieren und in einer Produktionsumgebung deaktivieren können, obwohl meine Tests so gut wie keine Auswirkungen auf die Leistung von Zusicherungen gezeigt haben.
In diesem Fall ist es in Ordnung, keine Zusicherungen zu verwenden, da der Code nur fehlschlägt. Dies passiert, wenn Sie Zusicherungen verwenden. Der einzige Unterschied besteht darin, dass es bei Behauptungen früher, aussagekräftiger und möglicherweise mit zusätzlichen Informationen geschehen kann, die Ihnen helfen können, herauszufinden, warum es passiert ist, wenn Sie es nicht erwartet haben.
(1) ist etwas schwieriger. Wenn Sie keine Kontrolle über den Code haben, den Sie aufrufen, stecken Sie fest. Wenn null eine gültige Antwort ist, müssen Sie danach suchen.
Wenn es sich jedoch um Code handelt, den Sie steuern (und dies ist häufig der Fall), ist dies eine andere Geschichte. Vermeiden Sie die Verwendung von Nullen als Antwort. Mit Methoden, die Sammlungen zurückgeben, ist es einfach: Leere Sammlungen (oder Arrays) anstelle von Nullen fast immer zurückzugeben.
Bei Nicht-Sammlungen kann es schwieriger sein. Betrachten Sie dies als Beispiel: Wenn Sie diese Schnittstellen haben:
public interface Action {
void doSomething();
}
public interface Parser {
Action findAction(String userInput);
}
Dabei nimmt Parser rohe Benutzereingaben entgegen und findet etwas zu tun, möglicherweise wenn Sie eine Befehlszeilenschnittstelle für etwas implementieren. Jetzt können Sie den Vertrag so gestalten, dass er null zurückgibt, wenn keine entsprechende Aktion ausgeführt wird. Das führt zu der Nullprüfung, von der Sie sprechen.
Eine alternative Lösung besteht darin, niemals null zurückzugeben und stattdessen das Null-Objektmuster zu verwenden :
public class MyParser implements Parser {
private static Action DO_NOTHING = new Action() {
public void doSomething() { /* do nothing */ }
};
public Action findAction(String userInput) {
// ...
if ( /* we can't find any actions */ ) {
return DO_NOTHING;
}
}
}
Vergleichen Sie:
Parser parser = ParserFactory.getParser();
if (parser == null) {
// now what?
// this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
// do nothing
} else {
action.doSomething();
}
zu
ParserFactory.getParser().findAction(someInput).doSomething();
Dies ist ein viel besseres Design, da es zu einem präziseren Code führt.
Das heißt, vielleicht ist es für die findAction () -Methode völlig angemessen, eine Ausnahme mit einer aussagekräftigen Fehlermeldung auszulösen - insbesondere in diesem Fall, in dem Sie sich auf Benutzereingaben verlassen. Es wäre viel besser, wenn die findAction-Methode eine Ausnahme auslösen würde, als wenn die aufrufende Methode mit einer einfachen NullPointerException ohne Erklärung in die Luft jagen würde.
try {
ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
userConsole.err(anfe.getMessage());
}
Oder wenn Sie der Meinung sind, dass der Try / Catch-Mechanismus zu hässlich ist, anstatt nichts zu tun, sollte Ihre Standardaktion dem Benutzer Feedback geben.
public Action findAction(final String userInput) {
/* Code to return requested Action if found */
return new Action() {
public void doSomething() {
userConsole.err("Action not found: " + userInput);
}
}
}