Der Fluch von null: Warum schreiben wir immer wieder dasselbe?
\n\nWenn Sie länger als eine Woche in Java programmieren, sind Sie mit Sicherheit schon auf NullPointerException gestoßen. Und höchstwahrscheinlich war Ihre erste Reaktion: „Aha, ich muss es einfach prüfen!“. So entstehen kilometerlange Codezeilen wie diese:
\n\nif (user != null) {\n if (user.getAddress() != null) {\n String city = user.getAddress().getCity();\n if (city != null) {\n System.out.println(city.toUpperCase());\n }\n }\n}\n\n\nDieser Code funktioniert. Aber er ist hässlich. Er verwandelt das Lesen der Logik in eine Qual und die Wartung in einen Albtraum. Die Frage „How do I avoid checking for nulls in Java?“ auf Stack Overflow hat nicht umsonst Tausende von Aufrufen gesammelt. Jeder Java-Entwickler stellt sie sich früher oder später.
\n\nDie gute Nachricht: Null-Prüfungen zu vermeiden ist nicht nur möglich, sondern auch notwendig. Java ist nicht JavaScript, und wir haben die Werkzeuge, um sauberen, sicheren Code ohne „schützende“ Konstrukte zu schreiben. Lassen Sie uns sie alle durchgehen – vom grundlegenden Handwerkszeug bis zu fortgeschrittenen Techniken.
\n\n1. Optional<T> – Ihr wichtigster Verbündeter
\n\nSeit Java 8 haben wir Optional. Es ist ein Container, der einen Wert enthalten oder leer sein kann. Es wurde buchstäblich entwickelt, um Null-Prüfungen zu vermeiden und Code deklarativer zu gestalten.
Wie funktioniert es?
\n\nAnstatt null aus einer Methode zurückzugeben, geben Sie Optional zurück. Und anstelle von Prüfungen verwenden Sie Methodenketten.
// Schlecht\npublic String getCity(User user) {\n if (user == null) return "Unknown";\n Address address = user.getAddress();\n if (address == null) return "Unknown";\n return address.getCity();\n}\n\n// Gut\npublic Optional<String> getCity(User user) {\n return Optional.ofNullable(user)\n .map(User::getAddress)\n .map(Address::getCity);\n}\n\n// Verwendung\nString city = getCity(someUser).orElse("Unknown");\n\n\nBeachten Sie: kein einziges if. map funktioniert nur, wenn der Wert nicht null ist. Wenn auf irgendeiner Stufe null auftritt – wird die Kette unterbrochen und Sie erhalten Optional.empty().
Beliebte Methoden von Optional
\n\n- \n
orElse(T other)– Wert oder Ersatzwert zurückgeben \n orElseGet(Supplier<? extends T> other)– faule Version, wird nur bei Bedarf ausgewertet \n orElseThrow(Supplier<? extends X> exceptionSupplier)– eigene Ausnahme werfen \n ifPresent(Consumer<? super T> consumer)– Aktion ausführen, wenn Wert vorhanden \n filter(Predicate<? super T> predicate)– nach Bedingung filtern \n
// Beispiel mit Filter\nOptional<String> name = Optional.of("Java");\nname.filter(n -> n.startsWith("J"))\n .ifPresent(System.out::println); // Gibt "Java" aus\n\n\nWichtig: Optional ist für Rückgabewerte gedacht, nicht für Klassenfelder oder Methodenparameter. Verwenden Sie es nicht übermäßig.
2. Objects.requireNonNull() – Hilfsmittel für explizite Verträge
\n\nManchmal ist null ein Fehler, der so früh wie möglich erkannt werden muss. Anstatt Prüfungen manuell zu schreiben, verwenden Sie java.util.Objects.requireNonNull().
public class UserService {\n private final UserRepository repository;\n\n public UserService(UserRepository repository) {\n this.repository = Objects.requireNonNull(repository, \n "UserRepository must not be null");\n }\n\n public User findById(Long id) {\n Objects.requireNonNull(id, "ID must not be null");\n return repository.findById(id);\n }\n}\n\n\nDieser Ansatz nennt