En évitant les!= null états
j'utilise object != null
beaucoup de choses à éviter NullPointerException
.
Est-il une bonne alternative?
par exemple:
if (someobject != null) {
someobject.doCalc();
}
cela évite un NullPointerException
, quand il est inconnu si l'objet est null
ou non.
notez que la réponse acceptée peut être périmée, voir https://stackoverflow.com/a/2386013/12943 pour un approche.
30 réponses
pour moi, cela ressemble à un problème assez courant auquel les développeurs débutants et intermédiaires ont tendance à faire face à un moment donné: ils ne savent pas ou ne font pas confiance aux contrats auxquels ils participent et ils surfacent défensivement pour des nulls. De plus, lorsqu'ils écrivent leur propre code, ils ont tendance à s'en remettre aux NULL pour indiquer quelque chose, ce qui oblige l'appelant à vérifier les null.
pour mettre cela d'une autre manière, il y a deux cas où la vérification nulle
-
où null est une réponse valable dans les termes du contrat; et
-
où ce n'est pas une réponse valable.
(2) est facile. Utilisez les énoncés assert
(assertions) ou autorisez l'échec (par exemple, NullPointerException ). Les Assertions sont une fonctionnalité Java très sous-utilisée qui a été ajoutée à la version 1.4. Le la syntaxe est:
assert <condition>
ou
assert <condition> : <object>
où <condition>
est une expression booléenne et <object>
est un objet dont la sortie de la méthode toString()
sera incluse dans l'erreur.
Un assert
déclaration déclenche une Error
( AssertionError
) si la condition n'est pas vrai. Par défaut, Java ignore les assertions. Vous pouvez activer les assertions en passant l'option -ea
à la JVM. Vous pouvez activer et désactiver les assertions pour les classes individuelles et les paquets. Cela signifie que vous pouvez valider le code avec les assertions tout en développant et en testant, et les désactiver dans un environnement de production, bien que mes tests ont montré à peu près aucun impact de performance des assertions.
ne pas utiliser d'assertions dans ce cas est correct parce que le code va tout simplement échouer, ce qui est ce qui se passera si vous utilisez des assertions. La seule différence est qu'avec des assertions, cela pourrait se produire plus tôt., en plus de façon significative et, éventuellement, avec des informations supplémentaires qui peuvent vous aider à comprendre pourquoi c'est arrivé si vous n'attendiez pas.
(1) est un peu plus difficile. Si vous n'avez aucun contrôle sur le code que vous appelez alors vous êtes coincé. Si null est une réponse valide, Vous devez vérifier.
Si c'est le code que vous ne contrôle cependant (et c'est souvent le cas), alors c'est une autre histoire. Évitez d'utiliser nulls comme réponse. Avec les méthodes que les collections de retour, il est facile: retourner les collections vides (ou tableaux) au lieu de nulls à peu près tout le temps.
avec des non-collections il 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 l'entrée brute de l'utilisateur et trouve quelque chose à faire, peut-être si vous mettez en œuvre une interface de ligne de commande pour quelque chose. Maintenant, vous pourriez rendre le contrat qu'il renvoie nul s'il n'y a pas les mesures appropriées. Cela mène à la vérification nulle dont vous parlez.
une solution alternative est de ne jamais retourner null et d'utiliser à la place le motif D'objet Null :
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();
qui est une conception beaucoup mieux parce qu'il conduit à un code plus concis.
cela dit, peut-être est-il entièrement approprié pour la méthode findAction() pour lancer une Exception avec un message d'erreur significatif -- surtout dans ce cas où vous comptez sur l'entrée de l'utilisateur. Il serait beaucoup mieux pour la méthode findAction de lancer une Exception que pour la méthode calling de faire exploser 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 de ne rien faire votre action par défaut devrait fournir une rétroaction à la 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);
}
}
}
si vous utilisez (ou prévoyez d'utiliser) un IDE Java comme JetBrains IntelliJ idée , Eclipse ou Netbeans ou un outil comme findbugs, alors vous pouvez utiliser des annotations pour résoudre ce problème.
en gros, vous avez @Nullable
et @NotNull
.
Vous pouvez utiliser dans la méthode et les paramètres, comme ceci:
@NotNull public static String helloWorld() {
return "Hello World";
}
ou
@Nullable public static String helloWorld() {
return "Hello World";
}
le second exemple ne compilera pas (in IntelliJ IDEA).
lorsque vous utilisez la première fonction helloWorld()
dans un autre morceau de code:
public static void main(String[] args)
{
String result = helloWorld();
if(result != null) {
System.out.println(result);
}
}
maintenant le compilateur D'idées IntelliJ vous dira que le chèque est inutile, puisque la fonction helloWorld()
ne retournera jamais null
, jamais.
utilisant le paramètre
void someMethod(@NotNull someParameter) { }
si vous écrivez quelque chose comme:
someMethod(null);
ça ne compile pas.
dernier exemple utilisant @Nullable
@Nullable iWantToDestroyEverything() { return null; }
cela
iWantToDestroyEverything().something();
Et vous pouvez être sûr que cela n'arrivera pas. :)
c'est une bonne façon de laisser le compilateur vérifier quelque chose de plus qu'il ne le fait habituellement et d'appliquer vos contrats pour être plus fort. Malheureusement, il n'est pas soutenu par tous les compilateurs.
dans IntelliJ idée 10.5 et on, ils ont ajouté le soutien pour toute autre mise en œuvre @Nullable
@NotNull
.
See blog post More flexible and configurable @null/@NotNull annotations .
si les valeurs nulles ne sont pas autorisées
si votre méthode est appelée extérieurement, commencez par quelque chose comme ceci:
public void method(Object object) {
if (object == null) {
throw new IllegalArgumentException("...");
}
Puis, dans le reste de cette méthode, vous saurez que object
n'est pas nulle.
si c'est une méthode interne (qui ne fait pas partie d'une API), il suffit de documenter qu'elle ne peut pas être nulle, et c'est tout.
exemple:
public String getFirst3Chars(String text) {
return text.subString(0, 3);
}
cependant, si votre méthode juste passe donc la valeur, et la prochaine méthode de passe etc. cela pourrait devenir problématique. Dans ce cas, vous pouvez vérifier l'argument comme ci-dessus.
si nul est autorisé
Cela dépend vraiment. Si je trouve que je fais souvent quelque chose comme ça:
if (object == null) {
// something
} else {
// something else
}
donc je branche, et je fais deux choses complètement différentes. Il n'y a pas d'extrait de code moche, parce que j'ai vraiment besoin de faire deux choses différentes selon les données. Exemple, devrais-je travailler sur l'entrée, ou dois-je calculer une bonne valeur par défaut?
il est en fait rare pour moi d'utiliser l'idiome" if (object != null && ...
".
il peut être plus facile de vous donner des exemples, si vous montrez des exemples où vous utilisez typiquement l'idiome.
Wow, je déteste presque ajouter une autre réponse quand nous avons 57 différentes façons de recommander le NullObject pattern
, mais je pense que certaines personnes intéressées par cette question peuvent aimer savoir qu'il y a une proposition sur la table pour Java 7 d'ajouter " manipulation nulle " -une syntaxe rationalisée pour la logique si-pas-égale-nulle.
L'exemple donné par Alex Miller ressemble à ceci:
public String getPostcode(Person person) {
return person?.getAddress()?.getPostcode();
}
le ?.
signifie seulement supprimer l'Identificateur de gauche s'il n'est pas nul, sinon évaluer le reste de l'expression comme null
. Certaines personnes, comme Dick Wall, membre du Java Posse et les électeurs de chez Devoxx aiment vraiment cette proposition, mais il y a aussi de l'opposition, au motif qu'elle encouragera en fait plus l'utilisation de null
comme valeur sentinelle.
mise à jour: An proposition officielle pour un null-sécurité de l'opérateur dans Java 7 a été présentée en vertu de Projet de Pièce de monnaie. La syntaxe est un peu différente de l'exemple ci-dessus, mais c'est la même notion.
mise à jour: la proposition de l'opérateur" null-safe " n'a pas été prise en compte dans le projet Coin. Donc, vous ne verrez pas cette syntaxe dans Java 7.
si des valeurs non définies ne sont pas autorisées:
vous pouvez configurer votre IDE pour vous prévenir d'un éventuel déréférencement nul. Par exemple: dans Eclipse, voir Préférences > Java > compilateur > erreurs / avertissements / analyse nulle .
si des valeurs non définies sont admises:
si vous voulez définir une nouvelle API où des valeurs non définies ont un sens , utilisez l'Option Pattern (peut être familier des langues fonctionnelles). Il présente les avantages suivants:
- il est explicitement indiqué dans l'API si une entrée ou une sortie existe ou non.
- , Le compilateur vous oblige à gérer le "undefined".
- Option est un monad , il n'y a donc pas besoin de vérification nulle verbeuse, il suffit d'utiliser map / foreach/getOrElse ou un combinateur similaire pour utiliser en toute sécurité la valeur (exemple) .
Java 8 a une classe intégrée Optional
(recommandé); pour les versions antérieures, il existe des alternatives de bibliothèque, par exemple Goyave 's Optional
ou FunctionalJava 's Option
. Mais comme beaucoup de modèles de style fonctionnel, l'utilisation de L'Option en Java (même 8) boilerplate, que vous pouvez réduire en utilisant un langage JVM moins verbeux, par exemple Scala ou Xtend.
si vous avez affaire à une API qui pourrait renvoyer nulls , vous ne pouvez pas faire grand chose en Java. Xtend et Groovy ont le Elvis operator ?:
et le null-safe dereference operator ?.
, mais notez que cela renvoie null en cas de référence nulle, de sorte qu'il ne fait que "retarder" la bonne manipulation de NULL.
seulement pour cette situation -
ne pas vérifier si une variable est nulle avant d'invoquer une méthode égale (un exemple de comparaison de chaîne de caractères ci-dessous):
if ( foo.equals("bar") ) {
// ...
}
donnera un NullPointerException
si foo
n'existe pas.
vous pouvez éviter que si vous comparez votre String
comme ceci:
if ( "bar".equals(foo) ) {
// ...
}
avec Java 8 vient la nouvelle classe java.util.Optional
qui résout sans doute une partie du problème. On peut au moins dire qu'il améliore la lisibilité du code, et dans le cas des API publiques rendre le contrat de L'API plus clair pour le développeur client.
ils travaillent comme ça:
un objet optionnel pour un type donné ( Fruit
) est créé comme type de retour d'une méthode. Il peut être vide ou contenir un objet Fruit
:
public static Optional<Fruit> find(String name, List<Fruit> fruits) {
for (Fruit fruit : fruits) {
if (fruit.getName().equals(name)) {
return Optional.of(fruit);
}
}
return Optional.empty();
}
regardez maintenant ce code où nous recherchons une liste de Fruit
( fruits
) pour une instance de Fruit donnée:
Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
Fruit fruit = found.get();
String name = fruit.getName();
}
vous pouvez utiliser l'opérateur map()
pour effectuer un calcul sur--ou extraire une valeur de--un objet optionnel. orElse()
vous permet de fournir un repli pour les valeurs manquantes.
String nameOrNull = find("lemon", fruits)
.map(f -> f.getName())
.orElse("empty-name");
bien sûr, le contrôle pour la valeur nulle / vide est toujours nécessaire, mais au moins le développeur est conscient que la valeur peut être vide, et le risque d'oublier de le vérifier est limitée.
dans une API construite à partir de zéro en utilisant Optional
chaque fois qu'une valeur de retour peut être vide, et en retournant un objet simple seulement quand il ne peut pas être null
(convention), le code client peut abandonner les contrôles nuls sur les valeurs de retour d'objet simple...
bien sûr Optional
pourrait également être utilisé comme un argument de méthode, peut-être un meilleur moyen d'indiquer arguments optionnels que 5 ou 10 méthodes de surcharge dans certains cas.
Optional
offre d'autres pratiques, telles que orElse
qui permettent l'utilisation d'une valeur par défaut, et ifPresent
qui fonctionne avec les expressions lambda .
je vous invite à lire cet article (ma principale source pour écrire cette réponse) dans lequel le NullPointerException
(et en général le pointeur null) problématique ainsi que la solution (partielle) introduit par Optional
a expliqué: Java Objets Facultatifs .
selon le type d'objets que vous vérifiez, vous pouvez être en mesure d'utiliser certaines des classes dans les Apache commons telles que: apache commons lang et apache commons collections
exemple:
String foo;
...
if( StringUtils.isBlank( foo ) ) {
///do something
}
ou (en fonction de ce que vous avez besoin de vérifier):
String foo;
...
if( StringUtils.isEmpty( foo ) ) {
///do something
}
la classe des StringUtils n'est qu'une parmi tant d'autres; il y a assez peu de bonnes classes aux communes que nul ne la manipulation en toute sécurité.
voici un exemple de la façon dont vous pouvez utiliser la vallidation nulle en JAVA lorsque vous incluez la bibliothèque apache(commons-lang-2.4.jar)
public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
return read(new StringReader(xml), true, validationEventHandler);
}
et si vous utilisez Spring, Spring a également la même fonctionnalité dans son paquet, voir bibliothèque(spring-2.4.6.jar)
exemple d'utilisation de cette classe statique F de spring (org.springframework.util.Assert)
Assert.notNull(validationEventHandler,"ValidationHandler not Injected");
- si vous considérez qu'un objet ne doit pas être nul (ou qu'il s'agit d'un bug), utilisez une assertion.
- si votre méthode n'accepte pas les valeurs null params, dites-le dans le javadoc et utilisez une assertion.
vous devez vérifier l'objet != null uniquement si vous souhaitez gérer le cas où l'objet peut être null...
il y a une proposition d'ajouter de nouvelles annotations dans Java7 pour aider avec les params null / notnull: http://tech.puredanger.com/java7/#jsr308
je suis fan du code" fail fast". Demandez - vous - faites-vous quelque chose d'utile dans le cas où le paramètre est nul? Si vous n'avez pas de réponse claire sur ce que votre code devrait faire dans ce cas... C'est-à-dire: il ne devrait jamais être nul en premier lieu, puis l'ignorer et permettre une NullPointerException à lancer. Le code appelant donnera autant de sens à un NPE qu'à une Illégalargumentexception, mais il sera plus facile pour le développeur de déboguer et de comprendre ce qui a mal tourné si un NPE est lancé plutôt que votre code en essayant d'exécuter une autre logique de contingence inattendue - ce qui se traduit finalement par un échec de l'application de toute façon.
le cadre de collections Google offre un bon et élégant moyen d'atteindre le contrôle nul.
il y a une méthode dans une classe de bibliothèque comme celle-ci:
static <T> T checkNotNull(T e) {
if (e == null) {
throw new NullPointerException();
}
return e;
}
et l'usage est (avec import static
):
...
void foo(int a, Person p) {
if (checkNotNull(p).getAge() > a) {
...
}
else {
...
}
}
...
ou dans votre exemple:
checkNotNull(someobject).doCalc();
parfois, vous avez des méthodes qui fonctionnent sur ses paramètres qui définissent une opération symétrique:
a.f(b); <-> b.f(a);
si vous savez que b ne peut jamais être null, vous pouvez simplement l'échanger. Il est le plus utile pour les égaux:
Au lieu de foo.equals("bar");
mieux faire "bar".equals(foo);
.
plutôt que le motif D'objet Null -- qui a ses utilisations -- vous pourriez considérer des situations où l'objet null est un bogue.
lorsque l'exception est lancée, examiner la trace de la pile et travailler à travers le bug.
Java 7 a une nouvelle classe d'utilité java.util.Objects
sur laquelle il existe une méthode requireNonNull()
. Tout ce que cela fait est de lancer un NullPointerException
si son argument est nul, mais il nettoie un peu le code. Exemple:
Objects.requireNonNull(someObject);
someObject.doCalc();
la méthode est la plus utile pour vérifier juste avant une affectation dans un constructeur, où chaque utilisation de celui-ci peut sauver trois lignes de code:
Parent(Child child) {
if (child == null) {
throw new NullPointerException("child");
}
this.child = child;
}
devient
Parent(Child child) {
this.child = Objects.requireNonNull(child, "child");
}
Null n'est pas un "problème". Il fait partie intégrante d'un ensemble d'outils de modélisation complet . Logiciel vise à modéliser la complexité du monde et nulle porte son fardeau. Null indique "Pas de données" ou "Inconnu" en Java et autres. Il est donc approprié d'utiliser des valeurs null pour ces fins. Je ne préfère pas le modèle' Null object'; je pense qu'il monte le ' qui gardera
les gardiens le " problème.
Si vous me demandez quel est le nom de ma petite amie, je vous dirai que je n'ai pas de petite amie. Dans le langage Java, je renvoie null.
Une alternative serait de lancer une exception significative pour indiquer un problème qui ne peut pas être (ou ne veut pas être) résolu juste là et le déléguer quelque part plus haut dans la pile pour réessayer ou signaler l'erreur d'accès aux données à l'utilisateur.
-
pour une "question inconnue" donner "réponse inconnue". (null-sûr, lorsque cela est correct du point de vue métier) la Vérification des arguments pour les nuls une fois à l'intérieur d'une méthode avant l'utilisation, soulage les multiples appels de vérification avant de les appeler.
public Photo getPhotoOfThePerson(Person person) { if (person == null) return null; // Grabbing some resources or intensive calculation // using person object anyhow. }
précédent conduit à un flux logique normal pour ne pas obtenir de photos d'une petite amie inexistante de ma photothèque.
getPhotoOfThePerson(me.getGirlfriend())
et il s'adapte avec la nouvelle API Java à venir (à venir)
getPhotoByName(me.getGirlfriend()?.getName())
alors que il est plutôt "flux normal d'affaires" de ne pas trouver la photo stockée dans le DB pour une personne, j'avais l'habitude d'utiliser des paires comme ci-dessous pour quelques autres cas
public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException public static MyEnum parseMyEnumOrNull(String value);
et n'hésitez pas à taper
<alt> + <shift> + <j>
(générer javadoc dans Eclipse) et à écrire trois mots supplémentaires pour vous API publique. Ce sera plus que suffisant pour tous, mais ceux qui ne lisent pas la documentation./** * @return photo or null */
ou
/** * @return photo, never null */
-
c'est un cas plutôt théorique et dans la plupart des cas, vous devriez préférer L'API Java null safe (au cas où il sera publié dans 10 ans), mais
NullPointerException
est la sous-classe d'unException
. C'est donc une forme deThrowable
qui indique les conditions qu'une application raisonnable pourrait vouloir attraper ( javadoc )! Pour utiliser le premier avantage des exceptions et séparer le code de traitement des erreurs du code "régulier" ( selon les créateurs de Java ), il est approprié, comme pour moi, d'attraperNullPointerException
.public Photo getGirlfriendPhoto() { try { return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName()); } catch (NullPointerException e) { return null; } }
des Questions pourraient se poser:
Q. Et si
getPhotoDataSource()
retourne null?
R. Cela dépend de la logique commerciale. Si Je ne trouve pas d'album de photos, Je ne vous montrerai pas de photos. Que faire si appContext n'est pas initialisé? La logique commerciale de cette méthode le supporte. Si la même logique doit être plus stricte, alors lancer un exception elle fait partie de la logique opérationnelle et une vérification explicite De null devrait être utilisée (cas 3). La nouvelle API Java Null-safe convient mieux ici pour spécifier sélectivement ce qui implique et ce qui n'implique pas d'initialiser pour être rapide en cas d'erreurs de programmeur.Q. le code redondant pourrait être exécuté et les ressources inutiles pourraient être saisies.
A. Cela pourrait se produire sigetPhotoByName()
essayait d'ouvrir une base de données connexion, créerPreparedStatement
et utiliser le nom de la personne comme paramètre SQL enfin. L'approche pour une question inconnue donne une réponse inconnue (cas 1) fonctionne ici. Avant de saisir des ressources, la méthode doit vérifier les paramètres et retourner le résultat "inconnu" si nécessaire.Q. Cette approche comporte une pénalité de rendement en raison de l'ouverture de la fermeture d'essai.
R. Le logiciel doit être facile à comprendre et à modifier d'abord. Seulement après cela, on pourrait penser à la performance, et seulement si nécessaire! et en cas de besoin! ( source ), et bien d'autres).PS. Cette approche sera aussi raisonnable à utiliser que le "code de traitement des erreurs 1519700920" distinct du "code régulier qu'il est raisonnable d'utiliser à un endroit quelconque. Prenons l'exemple suivant:
public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) { try { Result1 result1 = performSomeCalculation(predicate); Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty()); Result3 result3 = performThirdCalculation(result2.getSomeProperty()); Result4 result4 = performLastCalculation(result3.getSomeProperty()); return result4.getSomeProperty(); } catch (NullPointerException e) { return null; } } public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) { SomeValue result = null; if (predicate != null) { Result1 result1 = performSomeCalculation(predicate); if (result1 != null && result1.getSomeProperty() != null) { Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty()); if (result2 != null && result2.getSomeProperty() != null) { Result3 result3 = performThirdCalculation(result2.getSomeProperty()); if (result3 != null && result3.getSomeProperty() != null) { Result4 result4 = performLastCalculation(result3.getSomeProperty()); if (result4 != null) { result = result4.getSomeProperty(); } } } } } return result; }
PPS. Pour ceux qui sont rapides à descendre (et pas si vite à lire la documentation) Je voudrais comme pour dire que je n'ai jamais attrapé une exception de point-nul (NPE) dans ma vie. Mais cette possibilité était conçu intentionnellement par les créateurs Java parce que NPE est une sous-classe de
Exception
. Nous avons un précédent dans L'histoire de Java quandThreadDeath
est unError
non pas parce qu'il s'agit en fait d'une erreur d'application, mais uniquement parce qu'il n'était pas destiné à être attrapé! Combien de NPE convient pour être unError
queThreadDeath
! Mais il ne l'est pas. -
ne cochez" Aucune donnée " que si la logique commerciale l'implique.
public void updatePersonPhoneNumber(Long personId, String phoneNumber) { if (personId == null) return; DataSource dataSource = appContext.getStuffDataSource(); Person person = dataSource.getPersonById(personId); if (person != null) { person.setPhoneNumber(phoneNumber); dataSource.updatePerson(person); } else { Person = new Person(personId); person.setPhoneNumber(phoneNumber); dataSource.insertPerson(person); } }
et
public void updatePersonPhoneNumber(Long personId, String phoneNumber) { if (personId == null) return; DataSource dataSource = appContext.getStuffDataSource(); Person person = dataSource.getPersonById(personId); if (person == null) throw new SomeReasonableUserException("What are you thinking about ???"); person.setPhoneNumber(phoneNumber); dataSource.updatePerson(person); }
si appContext ou dataSource n'est pas initialisé unhandled runtime NullPointerException va tuer le thread courant et sera traité par Thread.defaultUncaughtExceptionHandler (pour que vous définissiez et utilisiez votre logger préféré ou autre notification mechanizm). Si elle n'est pas définie, ThreadGroup#uncaughtException imprimera stactrace au système err. On devrait surveiller le journal des erreurs de l'application et ouvrir la question Jira pour chaque exception non manipulée qui est en fait une erreur de l'application. Programmeur devrait corriger bug quelque part dans les trucs d'initialisation.
en fin de compte, la seule façon de résoudre complètement ce problème est d'utiliser un langage de programmation différent:
- dans Objective-C, vous pouvez faire l'équivalent d'invoquer une méthode sur
nil
, et absolument rien ne se passera. Cela rend la plupart des vérifications nulles inutiles, mais cela peut rendre les erreurs beaucoup plus difficiles à diagnostiquer. - In Nice , un langage dérivé de Java, il existe deux versions de tous types: a potentiellement nulle et une version non-null de version. Vous ne pouvez invoquer des méthodes que sur les types not-null. Les types potentiellement-null peuvent être convertis en types non-null par une vérification explicite De null. Il est ainsi beaucoup plus facile de savoir où des vérifications nulles sont nécessaires et où elles ne le sont pas.
Commun "problème" en Java, en effet.
D'abord, mes pensées sur ceci:
je considère qu'il est mauvais de" manger " quelque chose quand NULL a été passé où NULL n'est pas une valeur valide. Si vous ne quittez pas la méthode avec une sorte d'erreur, alors cela signifie que rien ne s'est mal passé dans votre méthode, ce qui n'est pas vrai. Ensuite vous retournez probablement null dans ce cas, et dans la méthode de réception vous vérifiez encore null, et il ne finit jamais, et vous finissez avec " si != null", etc..
donc, IMHO, null doit être une erreur critique qui empêche une exécution ultérieure (c'est-à-dire, où null n'est pas une valeur valide).
la façon dont je résous ce problème est la suivante:
tout D'abord, je suis cette convention:
- toutes les méthodes publiques / API toujours vérifier ses arguments pour null
- toutes les méthodes privées ne vérifient pas null puisqu'il s'agit de méthodes contrôlées (il suffit de laisser mourir avec nullpointer exception dans le cas où il n'a pas été traité ci-dessus)
- les seules autres méthodes qui ne vérifient pas null sont les méthodes d'utilité. Ils sont publics, mais si vous les appelez pour une raison quelconque, vous savez quels paramètres vous passez. C'est comme essayer de faire bouillir de l'eau dans la bouilloire sans fournir d'eau...
et enfin, dans le code, la première ligne de la méthode publique va comme ceci:
ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();
Note que addParam () retourne lui-même, de sorte que vous pouvez ajouter plus de paramètres à vérifier.
méthode validate()
lancera coché ValidationException
si l'un des paramètres est nul (coché ou non coché est plus un problème de conception/goût, mais mon ValidationException
est coché).
void validate() throws ValidationException;
le message contiendra le texte suivant si, par exemple, "plans" est nul:
valeur d'argument illégale nulle est rencontrée pour le paramètre [plans] "
comme vous pouvez le voir, la seconde valeur de la méthode addParam () (string) est nécessaire pour le message de l'utilisateur, parce que vous ne pouvez pas facilement détecter le nom de la variable passée, même avec la réflexion (pas sujet de ce post de toute façon...).
et oui, nous savons qu'au-delà de cette ligne, nous ne rencontrerons plus de valeur nulle, donc nous n'invoquerons que des méthodes sûres sur ces objets.
de Cette façon, le code est propre, facile à entretenir et lisible.
cette question indique que vous pourriez être intéressé par les stratégies de traitement des erreurs. L'architecte de votre équipe devrait décider comment travailler les erreurs. Il y a plusieurs façons de le faire:
-
"permettre aux Exceptions de se répercuter à travers - les attraper à la "boucle principale" ou dans une autre routine de gestion.
- vérifier les conditions d'erreur et les manipuler de façon appropriée
bien sûr que vous avez un regard sur la programmation orientée vers L'Aspect, aussi - ils ont des façons soignées d'insérer if( o == null ) handleNull()
dans votre bytecode.
en plus de l'utilisation de assert
vous pouvez utiliser ce qui suit:
if (someobject == null) {
// Handle null here then move on.
}
ce qui est légèrement mieux que:
if (someobject != null) {
.....
.....
.....
}
N'utilisez jamais null. Ne le permettrait pas.
dans mes classes, la plupart des champs et des variables locales ont des valeurs par défaut non nulles, et j'ajoute des énoncés de contrat (toujours-on affirme) partout dans le code pour s'assurer que cela est appliqué (car c'est plus succinct, et plus expressif que de le laisser apparaître comme un NPE et d'avoir à résoudre le numéro de ligne, etc.).
une Fois que j'ai adopté cette pratique, j'ai remarqué que les problèmes semblaient se corriger eux-mêmes. Vous attraperiez des choses beaucoup plus tôt dans le processus de développement juste par accident et réalisez que vous aviez un point faible.. et plus important encore.. cela aide à encapsuler les préoccupations des différents modules, les différents modules peuvent se "faire confiance" les uns aux autres, et ne plus polluer le code avec les constructions if = null else
!
il s'agit de la programmation défensive et les résultats dans le code beaucoup plus propre à long terme. Toujours épurer les données, par exemple ici en appliquant des normes rigides, et les problèmes vont loin.
class C {
private final MyType mustBeSet;
public C(MyType mything) {
mustBeSet=Contract.notNull(mything);
}
private String name = "<unknown>";
public void setName(String s) {
name = Contract.notNull(s);
}
}
class Contract {
public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }
}
Les contrats sont comme des mini-tests unitaires qui sont toujours en cours, même en production, et quand les choses échouent, vous savez pourquoi, plutôt qu'un NPE aléatoire, vous devez d'une façon ou d'une autre comprendre.
Guava, une bibliothèque centrale très utile de Google, dispose d'une API agréable et utile pour éviter les nulls. Je trouve UsingAndAvoidingNullExplained très utile.
comme expliqué dans le wiki:
Optional<T>
est une façon de remplacer un renvoi t nul par un renvoi valeur non null. Une mention facultative peut soit contenir une référence non nulle (auquel cas nous disons que la référence est "présente"), ou elle peut contenir rien (en dans ce cas, la référence est "absent"). Il n'est jamais dit à "contenir la valeur null."
Utilisation:
Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5
C'est un problème très courant pour chaque développeur Java. Il y a donc un support officiel en Java 8 pour résoudre ces problèmes sans code encombré.
Java 8 a introduit java.util.Optional<T>
. Il s'agit d'un conteneur qui peut ou non contenir une valeur non nulle. Java 8 a donné une façon plus sûre de gérer un objet dont la valeur peut être nulle dans certains cas. Il est inspiré des idées de Haskell et Scala .
en bref, la classe optionnelle inclut des méthodes pour traiter explicitement les cas où une valeur est présente ou absente. Cependant, l'avantage par rapport aux références nulles est que la classe optionnelle
dans l'exemple ci-dessus nous avons une usine de service à domicile qui renvoie une poignée à plusieurs appareils disponibles dans la maison. Mais ces services peuvent être ou ne pas être disponibles / fonctionnels; cela signifie qu'il peut en résulter une NullPointerException. Au lieu d'ajouter une condition nulle if
avant d'utiliser n'importe quel service, enveloppons-la Dans
WRAPPING TO OPTION
considérons une méthode pour obtenir une référence d'un service d'une usine. Au lieu de retourner la référence de service, envelopper avec optionnel. Il permet à L'utilisateur de L'API de savoir que le retour service peuvent ou peuvent ne pas être disponibles ou fonctionnelle, à l'utilisation, sur la défensive
public Optional<Service> getRefrigertorControl() {
Service s = new RefrigeratorService();
//...
return Optional.ofNullable(s);
}
Comme vous le voyez Optional.ofNullable()
fournit un moyen facile d'obtenir la référence enveloppé. Il y a une autre façon d'obtenir la référence de Optional, soit Optional.empty()
et Optional.of()
. Un pour retourner un objet vide au lieu de retordre null et l'autre pour envelopper un objet non-null, respectivement.
COMMENT ÉVITER UN CHÈQUE NUL?
Une fois que vous avez enveloppé un objet de référence, Optional fournit de nombreuses méthodes utiles pour invoquer des méthodes sur une référence enveloppée sans NPE.
Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);
facultatif.ifPresent invoque le consommateur donné avec une référence s'il s'agit d'une valeur non nulle. Sinon, il ne fait rien.
@FunctionalInterface
public interface Consumer<T>
représente une opération qui accepte un seul argument d'entrée et ne renvoie aucun résultat. Contrairement à la plupart des autres interfaces fonctionnelles, le consommateur est censé fonctionner via des effets secondaires.
Il est très propre et facile à comprendre. Dans l'exemple de code ci-dessus, HomeService.switchOn(Service)
est invoqué si la référence de maintien optionnelle est non-null.
nous utilisons très souvent l'opérateur ternaire pour vérifier la condition nulle et retourner une valeur alternative ou une valeur par défaut. Optionnel fournit une autre façon de gérer la même condition sans vérifier null. Facultatif.orElse (defaultObj) renvoie defaultObj si L'option a une valeur nulle. Nous allons utiliser dans notre échantillon code:
public static Optional<HomeServices> get() {
service = Optional.of(service.orElse(new HomeServices()));
return service;
}
Maintenant HomeServices.get() fait la même chose, mais dans une meilleure façon. Il vérifie si le service est déjà initialisé de pas. Si c'est alors retourner le même ou créer un nouveau service. Optional
enfin, voici notre NPE ainsi que le code nul:
import java.util.Optional;
public class HomeServices {
private static final int NOW = 0;
private static Optional<HomeServices> service;
public static Optional<HomeServices> get() {
service = Optional.of(service.orElse(new HomeServices()));
return service;
}
public Optional<Service> getRefrigertorControl() {
Service s = new RefrigeratorService();
//...
return Optional.ofNullable(s);
}
public static void main(String[] args) {
/* Get Home Services handle */
Optional<HomeServices> homeServices = HomeServices.get();
if(homeServices != null) {
Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
refrigertorControl.ifPresent(HomeServices::switchItOn);
}
}
public static void switchItOn(Service s){
//...
}
}
Le message complet est NPE ainsi que Null check-gratuit code ... vraiment? .
j'aime les articles de Nat Pryce. Voici les liens:
dans les articles Il ya aussi un lien vers un dépôt Git pour un Java peut-être Type que je trouve intéressant, mais je ne pense pas que cela seul pourrait diminuer la je vérifie le code bloat. Après avoir fait quelques recherche sur Internet, je pense != code null bloat pourrait être diminué principalement par une conception prudente.
j'ai essayé la NullObjectPattern
mais, pour moi, n'est pas toujours la meilleure façon d'aller. Il y a parfois des quand "pas d'action" n'est pas approprié.
NullPointerException
est une Exception D'exécution qui signifie que c'est la faute des développeurs et avec assez d'expérience, il vous dit exactement où est l'erreur.
maintenant à la réponse:
essayez de rendre tous vos attributs et ses accesseurs aussi privés que possible ou éviter de exposer les clients à tous. Vous pouvez avoir les valeurs d'argument dans le constructeur bien sûr, mais en réduisant la portée vous ne laissez pas la classe client passer une valeur invalide. Si vous avez besoin de modifier les valeurs, vous pouvez toujours créer un nouveau object
. Vous vérifiez les valeurs dans le constructeur seulement une fois et dans le reste des méthodes vous pouvez être presque sûr que les valeurs ne sont pas nulles.
bien sûr, l'expérience est la meilleure façon de comprendre et appliquer cette suggestion.
Byte!
probablement la meilleure alternative pour Java 8 ou plus récent est d'utiliser la classe Optional
.
Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);
c'est particulièrement pratique pour les longues chaînes de valeurs nulles possibles. Exemple:
Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
.map(f -> f.getBar())
.map(b -> b.getBaz())
.map(b -> b.getInt());
exemple sur la façon de jeter l'exception sur null:
Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);
Java 7 introduit la méthode Objects.requireNonNull
qui peut être pratique quand quelque chose doit être vérifié pour non nullness. Exemple:
String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();
puis-je y répondre de manière plus générale!
We usually face à ce problème lorsque les méthodes obtiennent les paramètres de la façon dont nous ne nous y attendions pas (mauvais appel de méthode est la faute du programmeur). Par exemple: vous attendez d'obtenir un objet, à la place vous obtenez un null. Vous vous attendez à obtenir une chaîne avec au moins un caractère, à la place vous obtenez une chaîne vide ...
il n'y a donc pas de différence entre:
if(object == null){
//you called my method badly!
}
ou
if(str.length() == 0){
//you called my method badly again!
}
ils veulent tous les deux s'assurer que nous avons reçu des paramètres valides, avant que nous ne fassions d'autres fonctions.
comme mentionné dans d'autres réponses, pour éviter les problèmes ci-dessus, vous pouvez suivre le Design by contract pattern. S'il vous plaît voir http://en.wikipedia.org/wiki/Design_by_contract .
pour mettre en œuvre Ce modèle en java, vous pouvez utiliser core des annotations java comme javax.annotation.NotNull ou l'utilisation plus sophistiquée des bibliothèques comme Hibernate Validator .
juste un échantillon:
getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)
Maintenant, vous pouvez développer en toute sécurité la fonction de base de votre méthode sans avoir besoin de vérifier les paramètres d'entrée, ils protègent vos méthodes des paramètres inattendus.
Vous pouvez aller plus loin et assurez-vous que seulement valide pojo pourrait être créé dans votre application. (échantillon du site de validation de l'hibernation)
public class Car {
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
@Min(2)
private int seatCount;
// ...
}
je méprise fortement les réponses qui suggèrent d'utiliser les objets nuls dans chaque situation. Ce modèle peut briser le contrat et enfouir les problèmes de plus en plus profond au lieu de les résoudre, sans mentionner que utilisé de façon inappropriée va créer une autre pile de code de Plaque de chaudrée qui nécessitera un entretien futur.
en réalité si quelque chose retourné d'une méthode peut être null et que le code appelant doit prendre une décision à ce sujet, il devrait y avoir un appel plus tôt qui assure état.
gardez également à l'esprit que le motif d'objet nul aura faim de mémoire s'il est utilisé sans soin. Pour cela - l'instance D'un NullObject doit être partagée entre les propriétaires, et ne pas être une instance unique pour chacun de ceux-ci.
aussi, je ne recommande pas d'utiliser ce modèle où le type est censé être une représentation de type primitif-comme des entités mathématiques, qui ne sont pas scalaires: vecteurs, matrices, des nombres complexes et POD (Plaine vieilles données) objets, qui sont destinés à contenir l'état sous forme de Java types intégrés. Dans ce dernier cas, vous finiriez par appeler les méthodes getter avec des résultats arbitraires. Par exemple, ce qui devrait une NullPerson.getName () retour de méthode?
il vaut la peine d'envisager de tels cas afin d'éviter des résultats absurdes.
- N'initialise jamais les variables à null.
- Si (1) n'est pas possible, initialiser toutes les collections et tableaux pour vider les collections/tableaux.
faire cela dans votre propre code et vous pouvez éviter != vérifications nulles.
la plupart du temps les vérifications null semblent garder les boucles sur les collections ou les tableaux, donc initialisez les vides, vous n'aurez pas besoin de vérifications null.
// Bad
ArrayList<String> lemmings;
String[] names;
void checkLemmings() {
if (lemmings != null) for(lemming: lemmings) {
// do something
}
}
// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = {};
void checkLemmings() {
for(lemming: lemmings) {
// do something
}
}
il y a un petit overhead là-dedans, mais ça vaut le coup pour un code plus propre et moins D'exceptions nulles.
C'est l'erreur la plus fréquente survenue pour la plupart des développeurs.
nous avons plusieurs façons de gérer cela.
approche 1:
org.apache.commons.lang.Validate //using apache framework
notNull (Object object, String message)
approche 2:
if(someObject!=null){ // simply checking against null
}
approche 3:
@isNull @Nullable // using annotation based validation
Approche 4:
// by writing static method and calling it across whereever we needed to check the validation
static <T> T isNull(someObject e){
if(e == null){
throw new NullPointerException();
}
return e;
}
public static <T> T ifNull(T toCheck, T ifNull) {
if (toCheck == null) {
return ifNull;
}
return toCheck;
}