Pour moi, cela ressemble à un problème assez courant auquel les développeurs juniors à intermédiaires ont tendance à faire face à un moment donné: ils ne connaissent pas ou ne font pas confiance aux contrats auxquels ils participent et vérifient de manière défensive les null. De plus, lors de l'écriture de leur propre code, ils ont tendance à compter sur le retour de null pour indiquer quelque chose, ce qui oblige l'appelant à vérifier les null.
Pour le dire autrement, il y a deux cas où la vérification nulle apparaît:
Lorsque null est une réponse valable en termes de contrat; et
Où ce n'est pas une réponse valide.
(2) est facile. Utilisez des assert
instructions (assertions) ou autorisez l'échec (par exemple, NullPointerException ). Les assertions sont une fonctionnalité Java très sous-utilisée qui a été ajoutée dans 1.4. La syntaxe est:
assert <condition>
ou
assert <condition> : <object>
où <condition>
est une expression booléenne et <object>
est un objet dont toString()
la sortie de la méthode sera incluse dans l'erreur.
Une assert
instruction renvoie un Error
( AssertionError
) si la condition n'est pas vraie. Par défaut, Java ignore les assertions. Vous pouvez activer les assertions en transmettant l'option -ea
à la JVM. Vous pouvez activer et désactiver les assertions pour des classes et des packages individuels. Cela signifie que vous pouvez valider le code avec les assertions lors du développement et des tests et les désactiver dans un environnement de production, bien que mes tests n'aient montré pratiquement aucun impact sur les performances des assertions.
Ne pas utiliser d'assertions dans ce cas est correct car le code échouera, ce qui se produira si vous utilisez des assertions. La seule différence est qu'avec les assertions, cela pourrait arriver plus tôt, d'une manière plus significative et éventuellement avec des informations supplémentaires, ce qui pourrait vous aider à comprendre pourquoi cela s'est produit si vous ne vous y attendiez pas.
(1) est un peu plus difficile. Si vous n'avez aucun contrôle sur le code que vous appelez, vous êtes bloqué. Si null est une réponse valide, vous devez la vérifier.
Si c'est du code que vous contrôlez cependant (et c'est souvent le cas), alors c'est une autre histoire. Évitez d'utiliser des valeurs nulles comme réponse. Avec les méthodes qui retournent des collections, c'est facile: renvoyez des collections vides (ou tableaux) au lieu de null à peu près tout le temps.
Avec des non-collections, cela pourrait être plus difficile. Considérez ceci comme un exemple: si vous avez ces interfaces:
public interface Action {
void doSomething();
}
public interface Parser {
Action findAction(String userInput);
}
où Parser prend les entrées utilisateur brutes et trouve quelque chose à faire, peut-être si vous implémentez une interface de ligne de commande pour quelque chose. Vous pouvez maintenant rendre le contrat qu'il renvoie nul s'il n'y a aucune action appropriée. Cela mène à la vérification nulle dont vous parlez.
Une solution alternative consiste à ne jamais retourner null et à utiliser à la place le modèle Null Object :
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;
}
}
}
Comparer:
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();
}
à
ParserFactory.getParser().findAction(someInput).doSomething();
ce qui est une bien meilleure conception car elle conduit à un code plus concis.
Cela dit, il est peut-être tout à fait approprié que la méthode findAction () lève une exception avec un message d'erreur significatif - en particulier dans ce cas où vous comptez sur une entrée utilisateur. Il serait beaucoup mieux pour la méthode findAction de lever une exception que pour la méthode appelante de sauter avec une simple NullPointerException sans explication.
try {
ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
userConsole.err(anfe.getMessage());
}
Ou si vous pensez que le mécanisme try / catch est trop laid, plutôt que Ne rien faire, votre action par défaut devrait fournir des commentaires à l'utilisateur.
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);
}
}
}