La malédiction de null : pourquoi écrivons-nous la même chose encore et encore ?
\n\nSi vous codez en Java depuis plus d'une semaine, vous avez forcément rencontré NullPointerException. Et, très probablement, votre première réaction a été : « Ah, il suffit de vérifier ! ». Ainsi naissent des kilomètres de code comme celui-ci :
\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\nCe code fonctionne. Mais il est laid. Il transforme la lecture de la logique en torture, et la maintenance en cauchemar. La question « How do I avoid checking for nulls in Java? » sur Stack Overflow a rassemblé des milliers de vues, et ce n'est pas un hasard. Chaque développeur Java se la pose tôt ou tard.
\n\nBonne nouvelle : éviter les vérifications de null est non seulement possible, mais aussi nécessaire. Java n'est pas JavaScript, et nous avons des outils pour écrire un code propre et sûr sans constructions « défensives ». Passons-les tous en revue — de la base aux techniques avancées.
\n\n1. Optional<T> — votre meilleur allié
\n\nDepuis Java 8, nous avons Optional. C'est un conteneur qui peut contenir une valeur ou être vide. Il est littéralement conçu pour éviter les vérifications de null et rendre le code plus déclaratif.
Comment ça marche ?
\n\nAu lieu de retourner null depuis une méthode, retournez Optional. Et au lieu de vérifications, utilisez des chaînes de méthodes.
// Mauvais\npublic String getCity(User user) {\n if (user == null) return "Inconnu";\n Address address = user.getAddress();\n if (address == null) return "Inconnu";\n return address.getCity();\n}\n\n// Bon\npublic Optional<String> getCity(User user) {\n return Optional.ofNullable(user)\n .map(User::getAddress)\n .map(Address::getCity);\n}\n\n// Utilisation\nString city = getCity(someUser).orElse("Inconnu");\n\n\nRemarquez : pas un seul if. map ne fonctionne que si la valeur n'est pas null. Si à n'importe quelle étape on rencontre null — la chaîne s'interrompt et vous obtenez Optional.empty().
Méthodes populaires d'Optional
\n\n- \n
orElse(T other)— retourner la valeur ou une alternative \n orElseGet(Supplier<? extends T> other)— version paresseuse, calculée seulement si nécessaire \n orElseThrow(Supplier<? extends X> exceptionSupplier)— lever votre propre exception \n ifPresent(Consumer<? super T> consumer)— exécuter une action si la valeur est présente \n filter(Predicate<? super T> predicate)— filtrer selon une condition \n
// Exemple avec filtre\nOptional<String> name = Optional.of("Java");\nname.filter(n -> n.startsWith("J"))\n .ifPresent(System.out::println); // Affiche "Java"\n\n\nImportant : Optional est conçu pour les valeurs de retour, pas pour les champs de classe ou les paramètres de méthode. N'en abusez pas.
2. Objects.requireNonNull() — utilitaire pour des contrats explicites
\n\nParfois, null est une erreur et doit être détecté le plus tôt possible. Au lieu d'écrire des vérifications manuellement, utilisez 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 ne doit pas être null");\n }\n\n public User findById(Long id) {\n Objects.requireNonNull(id, "L'ID ne doit pas être null");\n return repository.findById(id);\n }\n}\n\n\nCette approche s'appelle