Qu'est-ce qu'une NullPointerException et comment la corriger ?

La NullPointerException (souvent abrégée NPE) est la star des erreurs Java. Elle survient dès que votre code tente d'utiliser une référence qui pointe sur null comme si elle pointait sur un objet réel.

Le déclencheur le plus simple

String nom = null;
int longueur = nom.length();   // ❌ NullPointerException

nom ne pointe vers aucun objet : appeler length() dessus est impossible. La JVM lève immédiatement une exception.

Les cinq causes les plus courantes

  1. Variable non initialisée : un champ d'objet vaut null par défaut si vous oubliez de l'initialiser.
  2. Retour de méthode null : une méthode comme Map.get() renvoie null si la clé n'existe pas.
  3. Chaînage d'appels (null-chain) : user.getAddress().getCity() plante si getAddress() retourne null.
  4. Tableau / collection non initialisé : List<String> l; sans new ArrayList<>().
  5. Auto-unboxing : int i = integerQuiEstNull; lève NPE lors de la conversion.

Helpful NPE (Java 14+)

Avant Java 14, le message était vague :

Exception in thread "main" java.lang.NullPointerException
    at com.example.Main.process(Main.java:15)

Depuis Java 14, le message indique précisément quel accès est null :

Exception in thread "main" java.lang.NullPointerException:
    Cannot invoke "String.length()" because "user.address.city" is null
    at com.example.Main.process(Main.java:15)

Fonctionnalité activée par défaut à partir de Java 15.

Comment éviter les NPE

1. Vérifier avant d'utiliser

if (nom != null) {
    System.out.println(nom.length());
}

2. Inverser les comparaisons avec une constante

if (nom.equals("Alice")) { ... }           // ❌ NPE si nom == null
if ("Alice".equals(nom)) { ... }           // ✅ sûr
if (Objects.equals(nom, "Alice")) { ... }  // ✅ sûr aussi

3. Utiliser Optional pour les retours

public Optional<User> findById(long id) { ... }

findById(42)
    .map(User::getEmail)
    .ifPresent(System.out::println);

Le type même du retour oblige l'appelant à traiter le cas absent — plus de surprise silencieuse.

4. Valider en entrée

import java.util.Objects;

public void setEmail(String email) {
    this.email = Objects.requireNonNull(email, "email");
}

Échoue immédiatement et avec un message clair si le paramètre est null.

5. Utiliser les annotations @Nullable / @NonNull

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@NotNull
public String formatter(@NotNull String input, @Nullable String prefix) {
    return (prefix != null ? prefix : "") + input.trim();
}

IntelliJ et les outils d'analyse statique vous alertent sur tout usage suspect avant même l'exécution.

6. Collections et chaînes vides plutôt que null

// ❌ L'appelant doit tester null
public List<User> getUsers() { return null; }

// ✅ Retourne une liste vide immuable — toujours sûre à itérer
public List<User> getUsers() { return List.of(); }

Le piège de l'auto-unboxing

Map<String, Integer> scores = new HashMap<>();
int s = scores.get("alice");  // ❌ NPE — get() renvoie Integer qui est null

// ✅ Utiliser getOrDefault
int s = scores.getOrDefault("alice", 0);

Diagnostic d'une NPE existante

  1. Lire la stack trace du bas vers le haut — la première ligne est le point d'erreur.
  2. Le message (sur Java 15+) indique quel objet était null.
  3. Chercher dans le code qui a pu laisser la référence à null : méthode qui retourne null, champ jamais affecté, JSON mal parsé.
  4. Ajouter une validation ou un Optional au bon endroit.

Règle d'or

Ne retournez jamais null depuis une méthode publique. Retournez Optional.empty(), une collection vide ou une valeur par défaut. Cette seule discipline élimine 80 % des NPE dans une base de code.