99 votes

Vérifier les chaînes d'appels "get" pour les nullités

Disons que j'aimerais exécuter la commande suivante :

house.getFloor(0).getWall(WEST).getDoor().getDoorknob();

Pour éviter une NullPointerException, je devrais faire ce qui suit si :

if (house != null && house.getFloor(0) && house.getFloor(0).getWall(WEST) != null
  && house.getFloor(0).getWall(WEST).getDoor() != null) ...

Existe-t-il un moyen ou une classe Utils déjà existante qui permette de faire cela de manière plus élégante, disons quelque chose comme ce qui suit ?

checkForNull(house.getFloor(0).getWall(WEST).getDoor().getDoorknob());

155voto

Stas S Points 80

Au cas où vous ne peut pas éviter d'enfreindre la Loi de Déméter (LdD) comme indiqué dans la réponse choisie, et avec Java 8 présentation de En option Si vous n'êtes pas d'accord, il serait probablement préférable de traiter les valeurs nulles dans les chaînes d'obtention telles que la vôtre.

El Optional vous permettra de canaliser plusieurs opérations de mappage (qui contiennent des appels get) dans une rangée. La vérification des nuls est automatiquement gérée par le système.

Par exemple, lorsque les objets ne sont pas initialisés, aucun print() ne sera effectué et aucune exception ne sera levée. Tout sera géré en douceur sous le capot. Lorsque les objets sont initialisés, un print sera effectué.

System.out.println("----- Not Initialized! -----");

Optional.ofNullable(new Outer())
        .map(out -> out.getNested())
        .map(nest -> nest.getInner())
        .map(in -> in.getFoo())
        .ifPresent(foo -> System.out.println("foo: " + foo)); //no print

System.out.println("----- Let's Initialize! -----");

Optional.ofNullable(new OuterInit())
        .map(out -> out.getNestedInit())
        .map(nest -> nest.getInnerInit())
        .map(in -> in.getFoo())
        .ifPresent(foo -> System.out.println("foo: " + foo)); //will print!

class Outer {
    Nested nested;
    Nested getNested() {
        return nested;
    }
}
class Nested {
    Inner inner;
    Inner getInner() {
        return inner;
    }
}
class Inner {
    String foo = "yeah!";
    String getFoo() {
        return foo;
    }
}

class OuterInit {
    NestedInit nested = new NestedInit();
    NestedInit getNestedInit() {
        return nested;
    }
}
class NestedInit {
    InnerInit inner = new InnerInit();
    InnerInit getInnerInit() {
        return inner;
    }
}
class InnerInit {
    String foo = "yeah!";
    String getFoo() {
        return foo;
    }
}

Donc, avec votre chaîne de getters cela ressemblera à ceci :

Optional.ofNullable(house)
        .map(house -> house.getFloor(0))
        .map(floorZero -> floorZero.getWall(WEST))
        .map(wallWest -> wallWest.getDoor())
        .map(door -> wallWest.getDoor())

Le retour de celui-ci sera quelque chose comme Optional<Door> ce qui vous permettra de travailler de manière beaucoup plus sûre sans vous soucier des exceptions nulles.

25voto

Roman Seleznov Points 380

Afin de vérifier une chaîne d'obtention pour null vous pouvez avoir besoin d'appeler votre code à partir d'un fermeture . Le code de l'appel de fermeture ressemblera à ceci :

public static <T> T opt(Supplier<T> statement) {       
    try {
        return statement.get();
    } catch (NullPointerException exc) {
        return null;
    }   
}

Et vous l'appelez en utilisant la syntaxe suivante :

Doorknob knob = opt(() -> house.getFloor(0).getWall(WEST).getDoor().getDoorknob());

Ce code est également sans danger pour les types et fonctionne en général comme prévu :

  1. Renvoie une valeur réelle du type spécifié si tous les objets de la chaîne ne sont pas null .
  2. Renvoie à null si l'un des objets de la chaîne est null .

Vous pouvez placer opt dans la classe util partagée et l'utiliser partout dans votre application.

12voto

Jerod Houghtelling Points 2426

Le meilleur moyen serait de éviter la chaîne. Si vous n'êtes pas familier avec la loi de Demeter (LoD), à mon avis vous devriez. Vous avez donné un exemple parfait d'une chaîne de messages qui est trop intime avec des classes dont elle n'a rien à connaître.

La loi de Déméter : http://en.wikipedia.org/wiki/Law_of_Demeter

9voto

Carl Manaster Points 23696

Vous pourriez bien sûr simplement envelopper l'expression entière dans un bloc try-catch, mais c'est une mauvaise idée. Une solution plus propre est la méthode Modèle d'objet nul . Ainsi, si votre maison n'a pas d'étage 0, elle renvoie un étage qui se comporte comme un étage normal, mais qui n'a pas de contenu réel ; les étages, lorsqu'on leur demande des murs qu'ils n'ont pas, renvoient des murs similaires "Nuls", etc.

4voto

Bozho Points 273663

Assurez-vous que les choses qui ne peuvent pas logiquement être null ne le sont pas. Par exemple, une maison a toujours un mur ouest. Afin d'éviter de telles exceptions dans l'état, vous pouvez avoir des méthodes pour vérifier si l'état que vous attendez est présent :

if (wall.hasDoor()) {
   wall.getDoor().etc();
}

Il s'agit essentiellement d'un contrôle de nullité, mais ce n'est pas toujours le cas.

Le fait est que vous devriez faire quelque chose au cas où vous auriez une null . Par exemple - return ou lancer un IllegalStateException

Et ce que vous ne devez pas faire - ne pas attraper. NullPointerException . Les exceptions d'exécution ne sont pas à attraper - il n'est pas prévu que vous puissiez les récupérer, et ce n'est pas une bonne pratique de s'appuyer sur les exceptions pour le flux logique. Imaginez que vous ne vous attendiez pas à ce que quelque chose soit null et vous attrapez (et enregistrez) un NullPointerException . Ces informations ne seront pas très utiles, car beaucoup de choses peuvent être null à ce moment-là.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X