NullPointerException est l'une des causes de défaillance de programmes Java les plus répandues.
Dans les cas les plus simples, le compilateur peut vous avertir directement lorsqu'il voit du code similaire à l'exemple suivant :
Object o = null;
String s = o.toString();
En cas de branchement/d'exécution en boucle et d'émission d'exceptions relativement sophistiquées, une analyse de flux s'avère nécessaire si vous voulez vérifier qu'une variable en cours de déréférencement a été affectée à une valeur null/non-null sur l'ensemble ou une partie des chemins dans le programme.
En raison de la complexité inhérente, il est préférable d'effectuer l'analyse de flux en procédant par petits blocs. L'analyse d'une méthode à la fois peut générer de bons résultats (le compilateur Java Eclipse ne prend pas en charge les analyse système complètes). Avantage : l'analyse est rapide et peut être effectuée par incréments (ce qui permet au compilateur de vous avertir directement pendant que vous entrez le code). Inconvénient : l'analyse ne peut pas "voir" quelles valeurs (null ou non-null) passent entre les méthodes (paramètres et valeurs de retour, par exemple).
C'est là qu'entrent en jeu les annotations null.
En spécifiant un paramètre de méthode tel que @NonNull, vous pouvez indiquer au compilateur que vous ne souhaitez pas avoir de valeur null dans cette position.
String capitalize(@NonNull String in) {
return in.toUpperCase(); // no null check required
}
void caller(String s) {
if (s != null)
System.out.println(capitalize(s)); // preceding null check is required
}
Dans la même veine que la conception par contrat DbC (Design-by-Contract), il y a deux parties en jeu :
capitalize a la garantie que l'argument in n'est pas null et que le déréférencement sans contrôle null est OK.
Dans le cas des valeurs de retour de méthodes, la situation est symétrique :
@NonNull String getString(String maybeString) {
if (maybeString != null)
return maybeString; // the above null check is required
else
return "<n/a>";
}
void caller(String s) {
System.out.println(getString(s).toUpperCase()); // no null check required
}
Le compilateur Java Eclipse peut être configuré afin qu'il utilise trois types d'annotation distincts pour son analyse null améliorée (activée par défaut) :
@NonNull : la valeur null n'est pas une valeur légale@Nullable : la valeur null est autorisée et doit être attendue@NonNullByDefault : les types des signatures de méthode qui ne possèdent pas d'annotation null sont considérés comme des valeurs non-null.Les annotations @NonNull et @Nullable sont prises en charge avec :
L'annotation @NonNullByDefault est prise en charge avec :
package-info.java) - pour affecter l'ensemble des types figurant dans le package
Même si les noms qualifiés de ces annotations sont
configurables, ce sont les noms par défaut indiqués ci-dessus qui sont utilisés (ceux du package org.eclipse.jdt.annotation).
Lors de l'utilisation d'annotations de type NULL tierces, veuillez vérifier qu'elles sont
correctement définies à l'aide d'au moins une meta annotation @Target car, dans le cas contraire,
le compilateur ne peut pas distinguer les annotations de déclaration (Java 5) des annotations de type
(Java 8).
Un fichier JAR comportant les annotations null par défaut est fourni avec Eclipse dans le fichier eclipse/plugins/org.eclipse.jdt.annotation_*.jar. Ce fichier JAR doit être placé dans le chemin de génération pour la compilation, mais pas nécessairement pour l'exécution (vous n'êtes donc pas obligé de le fournir aux utilisateurs de votre code compilé).
Au démarrage d'Eclipse Luna, deux versions de ce fichier JAR existent, l'une contenant des annotations de déclaration à utiliser dans Java 7 ou une version antérieure (version 1.1.x) et l'autre contenant des annotations de type NULL à utiliser dans Java 8 (version 2.0.x).
Pour les projets Java standard, il existe également un correctif rapide
pour les références non résolues avec les annotations @NonNull, @Nullable ou
@NonNullByDefault qui ajoute la version adaptée du fichier JAR au chemin de génération :

Pour les bundles/plug-ins OSGi, veuillez ajouter l'une des entrées suivantes au fichier MANIFEST.MF :
Require-Bundle: ..., org.eclipse.jdt.annotation;bundle-version="[1.1.0,2.0.0)";resolution:=optional
Require-Bundle: ..., org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional
Voir également la discussion dans la section correspondante relative à la compatibilité.
Il doit être évident maintenant que les annotations null ajoutent davantage d'informations à votre programme Java (qui peuvent ensuite être utilisées par le compilateur afin de fournir des avertissements améliorés). Mais que voulons-nous faire avec ces annotations ? D'un point de vue pragmatique, au moins trois raisons motivent l'utilisation des annotations null :
Concernant la raison (1), vous pouvez commencer à utiliser les annotations null sur le champ sans lire la suite, mais ne vous attendez pas à obtenir d'autres suggestions plus tard. Les autres raisons nécessitent quelques explications.
A première vue, l'utilisation d'annotations null pour les spécifications d'API dans la veine de la conception par contrat signifie simplement que les signatures de toutes les méthodes d'API doivent être entièrement annotées.
A l'exception des types de primitive comme int, chaque paramètre et chaque type de retour de méthode doivent être marqués par l'annotation @NonNull ou @Nullable.
Comme cela sous-entend l'insertion de nombreuses annotations null, il est bon de savoir que dans du code bien conçu (plus spécialement dans les méthodes d'API), l'annotation @NonNull est nettement plus fréquente que @Nullable. Le nombre d'annotations peut être réduit en déclarant l'annotation @NonNull comme annotation par défaut en utilisant une annotation @NonNullByDefault au niveau du package.
Différence notable entre l'annotation @Nullable et l'omission d'une annotation nulle :
cette annotation spécifie de manière explicite que la valeur null est OK et doit être attendue.
Par contraste, aucune annotation signifie simplement que nous ne savons pas quelle est l'intention.
C'est une ancienne situation où les deux parties (l'appelant et l'appelé) effectuent parfois un contrôle null de manière redondante et où les deux parties supposent parfois injustement que l'autre partie effectuera le contrôle.
C'est de là qu'est née l'annotation NullPointerExceptions.
En l'absence d'annotation, le compilateur ne dispensera pas de conseil spécifique, mais chaque suppression de référence non contrôlée sera marquée par une annotation @Nullable.
A l'aide des informations de base, nous pouvons mapper directement toutes les annotations de paramètre avec des pré-conditions et interpréter les annotations de retour sous la forme de post-conditions de la méthode.
Dans la programmation orientée objets, la notion de conception par contrat doit gérer une dimension supplémentaire : le sous-typage et la substitution (dans le reste du document, le terme "substituer" est utilisé au sens de l'annotation @Override dans Java 6 : des méthodes qui substituent ou implémentent une autre méthode d'un supertype). Prenons l'exemple d'un client qui appelle une méthode :
@NonNull String checkedString(@Nullable String in)
Nous partons du principe que toutes les implémentations de cette méthode respectent le contrat.
Si la déclaration de méthode réside dans une interface I1,
nous devons déclarer que n'importe quelle Cn implémentant I1 constitue une implémentation incompatible. Plus précisément, il est illégal qu'une classe Cn tente de substituer cette méthode au moyen d'une implémentation qui déclare le paramètre en tant que valeur @NonNull.
Si nous autorisons ce comportement, un module client programmé avec I1 pourrait légalement passer une valeur null comme argument, mais l'implémentation supposerait une valeur non-null -
une déréférence non contrôlée à l'intérieur de l'implémentation de méthode serait autorisée, mais bloquerait à l'exécution.
C'est pourquoi une spécification de paramètre @Nullable oblige toutes les substitutions
à admettre la valeur null comme valeur légale attendue.
Inversement, une spécification de retour @NonNull oblige toutes les substitutions
à s'assurer qu'aucune valeur null ne sera jamais renvoyée.
C'est pourquoi le compilateur doit vérifier qu'aucune substitution n'ajoute d'annotation de paramètre @NonNull (ou d'annotation de retour @Nullable) qui n'existait pas dans le supertype.
Ce qui est intéressant, c'est que les redéfinitions inverses sont légales : l'ajout d'une annotation de paramètre @Nullable ou d'une annotation de retour @NonNull (vous pouvez les considérer comme des "améliorations" de la méthode, elle accepte davantage de valeurs et génère une valeur de retour plus spécifique).
En forçant les sous-classes à répéter des annotations null dans des méthodes de remplacement, le contrat null de chaque méthode peut être compris sans qu'il ne soit nécessaire de rechercher la hiérarchie d'héritage. Toutefois, dans les cas où une hiérarchie d'héritage mêle du code de provenances différentes, il peut être impossible d'ajouter des annotations null à toutes les classes en une fois. Si tel est le cas, vous pouvez indiquer au compilateur qu'il doit traiter les méthodes dans lesquelles il manque des annotations null comme si les annotations d'une méthode remplacée étaient héritées. Pour ce faire, utilisez l'option de compilateur Hériter des annotations null. Il se peut qu'une méthode remplace deux méthodes dont les contrats null sont différents. De plus, une valeur de type null par défaut peut être appliquée dans une méthode en conflit avec une annotation null héritée. Ces cas sont signalés en tant qu'erreurs et la méthode de remplacement doit utiliser une annotation null explicite pour résoudre le conflit.
@NonNull en ne le spécifiant pas ?
Si l'héritage d'annotations null n'est pas activé, une situation particulière peut être sûre du point de vue théorique des types mais tout de même indiquer un problème : imaginez une superméthode qui déclare un paramètre comme @NonNull et une méthode de remplacement qui ne contraint pas le paramètre correspondant (ni par une annotation null explicite ni par une annotation @NonNullByDefault applicable).
Cette situation est sûre car les clients qui voient la superdéclaration seront forcés d'éviter null alors que l'implémentation de remplacement ne peut simplement pas optimiser cette garantie en raison d'un manque de spécification dans cette méthode spécifique.
Elle peut tout de même générer des conceptions non appropriées car le fait d'appliquer la déclaration dans le supertype à tous les remplacements peut être intentionnel.
Pour cette raison, le compilateur met à disposition l'option Paramètre '@NonNull' non annoté dans la méthode de remplacement :
null doit être acceptable, il est recommandé d'ajouter une annotation @Nullable afin de remplacer @NonNull dans la superméthode.
Les remarques précédentes ajoutent une difficulté lorsque le code annoté est écrit sous la forme d'un sous-type
d'un type "existant" (c'est-à-dire non annoté) (qui peut être hérité d'une bibliothèque tierce et qui ne peut donc pas être changé).
Si vous lisez la dernière section avec attention, vous vous êtes peut-être rendu compte que nous ne pouvons pas admettre qu'une méthode "existante" soit substituée par une méthode au moyen d'un paramètre @NonNull
(étant donné que les clients utilisant le supertype ne "voient" pas l'obligation @NonNull).
Dans cette situation, vous serez obligé d'omettre les annotations null (l'ajout d'annotations dans les bibliothèques après le fait a bien été envisagée, mais personne n'a actuellement garanti si une telle fonction sera effectivement disponible et, si oui, à quel moment).
La situation s'annonce délicate, si un sous-type d'un type "existant" réside dans un package pour lequel une annotation
@NonNullByDefault a été spécifiée. Désormais, un type avec un supertype non annoté aurait besoin de marquer l'ensemble des paramètres des méthodes de substitution avec l'annotation @Nullable :
même l'omission d'annotations de paramètre n'est pas autorisée vu que cela serait interprété comme un paramètre @NonNull, ce qui est interdit dans cette position.
C'est pourquoi le compilateur Java Eclipse prend en charge l'annulation d'une valeur null par défaut : en annotant une méthode ou un type avec une annotation @NonNullByDefault(false), une valeur par défaut applicable sera annulée pour cet élément et les paramètres non annotés seront à nouveau interprétés comme n'étant pas spécifiés. Maintenant, le sous-typage est à nouveau légal sans ajouter d'annotations @Nullable indésirables :
class LegacyClass {
String enhance (String in) { // clients are not forced to pass nonnull.
return in.toUpperCase();
}
}
@NonNullByDefault
class MyClass extends LegacyClass {
// ... methods with @NonNull default ...
@Override
@NonNullByDefault(false)
String enhance(String in) { // would not be valid if @NonNullByDefault were effective here
return super.enhance(in);
}
}
Les annotations Null fonctionnent mieux lorsqu'elles sont appliquées dans des signatures de méthode (en général, les variables locales n'en ont pas besoin, mais peuvent optimiser les annotations null pour établir un pont entre le code annoté et le code "existant"). Dans ce cas, les annotations null connectent les blocs d'analyse intra-procédurale pour obtenir des instructions concernant les flux de données globaux. Depuis Eclipse Kepler, les annotations null peuvent aussi être appliquées à des zones, mais dans ce cas, la situation est légèrement différente.
Prenez une zone associée à la marque @NonNull : celle-ci requiert clairement que toute affectation à la zone soit une valeur différente de Null.
De plus, le compilateur doit pouvoir vérifier qu'une zone requérant une valeur différente de Null n'est jamais accessible dans son état non initialisé (où elle est encore associée à la valeur null).
S'il s'avère que chaque constructeur est conforme à cette règle (de même, une zone statique doit avoir un initialiseur), le programme est assuré que le déréférencement de la zone ne peut jamais entraîner d'exception de type NullPointerException.
La situation est plus délicate si l'on considère une zone associée à la marque @Nullable.
Une telle zone doit toujours être considérée comme dangereuse et il est recommandé pour l'utilisation des zones admettant la valeur Null de toujours affecter la valeur à une variable locale avant de l'utiliser.
Avec une variable locale, l'analyse de flux peut indiquer exactement si une déréférence est suffisamment protégée par un contrôle null. Si vous suivez cette règle générale lors de l'utilisation de zones admettant la valeur Null, vous ne rencontrerez pas de problème.
Les choses se compliquent si le code déréférence directement la valeur d'une zone admettant la valeur Null. Le problème consiste en ce que tout contrôle null que le code peut effectuer avant la déréférence peut être facilement invalidé par :
Il est facile de constater que, sans analyser la synchronisation des unités d'exécution (qui ne fait pas partie des capacités du compilateur), aucun contrôle null concernant une zone admettant la valeur Null ne garantira la sécurité d'une déréférence suivante à 100 %. Par conséquent, si l'accès concurrent à une zone admettant la valeur Null est possible, la valeur de la zone ne doit jamais être déréférencée directement et une variable locale doit toujours être utilisée à la place. Même si l'accès concurrent n'est pas impliqué, d'autres questions posent un défi dans le cadre d'une analyse complète, qui est plus difficile que ce qu'un compilateur peut traiter.
Etant donné que le compilateur ne peut pas analyser entièrement les effets du crénelage, les effets secondaires et l'accès concurrent, le compilateur Eclipse ne procède pas une analyse de flux pour les zones (autre que relative à l'initialisation). Comme de nombreux développeurs considéreront cette limitation comme trop restrictive (la nécessité d'utiliser des variables locales lorsque les développeurs ont l'impression que leur code est en fait sécurisé), une nouvelle option a été introduite dans le but de parvenir à un compromis :
Le compilateur peut être configuré en vue de l'exécution d'une analyse syntaxique. Celle-ci détecte les modèles les plus évidents, tels que :
@Nullable Object f;
void printChecked() {
if (this.f != null)
System.out.println(this.f.toString());
}
Si cette option est activée, le code ci-dessus n'est pas repéré par le compilateur. Il est important de noter que cette analyse syntaxique n'est pas du tout "intelligente". Si du code apparaît entre le contrôle et la déréférence, le compilateur "oublie" lâchement les informations du contrôle null précédent, sans même essayer de déterminer si le code intermédiaire est inoffensif selon certains critères. Par conséquent, sachez qu'à chaque fois que le compilateur signale qu'une déréférence d'une zone admettant la valeur Null n'est pas sûre, même si vous pensez que la valeur Null n'a pas de raison de survenir, réécrivez votre code pour suivre étroitement le modèle reconnu mentionné plus haut, ou mieux, utilisez une variable locale pour optimiser la sophistication de l'analyse de flux qu'une analyse syntaxique n'atteindra jamais.
L'utilisation d'annotations null dans le style de la conception par contrat (tel que décrit ci-dessus) permet d'améliorer la qualité de votre code Java de différentes façons : au niveau de l'interface entre les méthodes, on déduit de manière explicite quels paramètres/valeurs de retour tolèrent une valeur null ou non. Cela capture les décisions de conception, qui sont extrêmement importantes pour les développeurs, d'une manière également vérifiable par le compilateur.
Grâce à cette spécification d'interface intraprocédurale, l'analyse de flux peut sélectionner des informations disponibles et fournir des erreurs/avertissements plus précis. En l'absence d'annotations, toutes les valeurs passant à l'intérieur et à l'extérieur d'une méthode sont des valeurs null inconnues et l'analyse null reste donc silencieuse concernant leur utilisation. Avec des annotations null au niveau de l'API, le caractère null de la plupart des valeurs est connu et le compilateur ne détectera qu'un très petit nombre d'exceptions de pointeur null (NPE, Null Pointer Exception). Néanmoins, vous devez garder en tête qu'il persiste tout de même quelques failles où des valeurs non spécifiées sont injectées dans l'analyse, ce qui empêche toute déclaration complète même si des NPE peuvent se produire à l'exécution.
La prise en charge d'annotations NULL a été conçue de manière à être compatible avec une extension ultérieure. Cette extension fait partie intégrante du langage Java en tant qu'annotations de type (JSR 308) et a été introduite dans Java 8. JDT prend en charge l'optimisation du nouveau concept d'annotations de type NULL.
Les détails sémantiques de l'analyse NULL basée sur l'annotation sont présentés ici, accompagnés d'une explication des règles que le compilateur vérifie et des messages qu'il génère en cas de violation d'une règle.
Sur la page de préférences correspondante, les règles individuelles vérifiées par le compilateur sont regroupées sous les en-têtes suivants :
En cas de violation de spécification, nous traitons les situations où une annotation null
dépose une réclamation en reportant une violation par l'implémentation actuelle. La situation typique résulte de la spécification d'une valeur (variable locale, argument, valeur de retour de méthode) en tant que @NonNull alors que l'implémentation fournit en fait une valeur nullable. Ici, une expression est considérée comme nullable si
elle est statiquement connue pour évaluer en tant que valeur null ou si elle est déclarée avec une annotation @Nullable.
Deuxièmement, ce groupe contient également les règles de substitution de méthode (voir ci-dessus).
Ici, une super méthode établit une réclamation (indiquant que la valeur null est un argument légal, par exemple)
tandis qu'une substitution tente de se soustraire à cette réclamation (en partant du principe que la valeur null n'est pas un argument légal).
Comme nous l'avons indiqué, même la spécialisation d'un argument non annoté en valeur @NonNull
constitue une violation de spécification, car elle implique un contrat qui doit relier le client
(certifiant de ne pas passer de valeur null), mais un client utilisant le supertype ne verra même pas ce contrat.
Il ne sait donc même pas ce qu'on attend de lui.
Pour obtenir la liste complète des situations portant sur les violations de spécification, reportez-vous
ici.
Il est important de comprendre que les erreurs dans ce groupe ne doivent jamais être ignorées. Sinon,
l'analyse null complète sera effectuée à partir de suppositions fausses.
Plus précisément, chaque fois que le compilateur voit une valeur assortie d'une annotation @NonNull, il part du principe qu'aucune valeur null ne se présentera à l'exécution.
Ce sont les règles concernant les violations de spécification qui garantissent que ce raisonnement est correct.
C'est pourquoi il est fortement recommandé de laisser ce type de problème configuré en tant qu'erreurs.
Par ailleurs, ce groupe de règles vérifie le respect des spécifications null.
Néanmoins, nous sommes confrontés ici à des valeurs qui ne sont pas déclarées comme @Nullable (ni la valeur null proprement dit); il s'agit de valeurs où l'analyse de flux intraprocédurale
déduit que des valeurs null peuvent se présenter sur certains chemins d'exécution.
Cette situation provient du fait que pour les variables locales non annotées le compilateur ne déduit pas si des valeurs null sont possible en utilisant son analyse de flux. En supposant que cette analyse est précise, il voit qu'un problème a la même gravité que des violations directes d'une spécification null. C'est pourquoi il est là aussi fortement recommandé de laisser ce problème configuré en tant qu'erreurs et de ne pas ignorer ces messages.
La création d'un groupe distinct pour ces problèmes a deux rôles : elle permet de documenter qu'un problème donné a été détecté à l'aide de l'analyse de flux, mais aussi de tenir compte du fait que cette analyse de flux pourrait comporter une défaillance (en raison d'un bogue dans l'implémentation). En cas de bogue connu dans l'implémentation, il pourrait être possible dans des situations exceptionnelles de supprimer un message d'erreur de ce genre.
Etant donné la nature des analyses statiques, l'analyse de flux peut ne pas voir qu'une certaine combinaison de chemins et valeurs d'exécution n'est pas possible. Prenons comme exemple la corrélation de variables suivante :
String flatten(String[] inputs1, String[] inputs2) {
StringBuffer sb1 = null, sb2 = null;
int len = Math.min(inputs1.length, inputs2.length);
for (int i = 0; i < len; i++) {
if (sb1 == null) {
sb1 = new StringBuffer();
sb2 = new StringBuffer();
}
sb1.append(inputs1[i]);
sb2.append(inputs2[i]); // warning here
}
if (sb1 != null) return sb1.append(sb2).toString();
return "";
}
Le compilateur reportera un accès de pointeur null potentiel lors de l'appel de la méthode sb2.append(..).
Le lecteur humain peut voir qu'il n'y a pas de danger actuel, car sb1 et sb2 se corrèlent d'une telle manière que les deux variables sont soit null soit non null.
A la ligne en question, nous savons que sb1 n'est pas null (sb2 n'est donc pas null non plus). Sans chercher à expliquer en détails pourquoi cette analyse de corrélation dépasse les capacités du compilateur Java Eclipse, gardez juste en tête que cette analyse n'a pas le pouvoir d'un démonstrateur de théorème et reporte donc de manière pessimiste certains problèmes qu'une analyse plus puissante pourrait certainement identifier comme de fausses alarmes.
Si vous souhaitez utiliser une analyse de flux, nous vous conseillons de donner un petit coup de main au compilateur afin qu'il puisse "voir" vos conclusions. Vous pouvez par exemple tout simplement fractionner if (sb1 == null)
en deux blocs if distincts (un pour chaque variable locale), ce qui demande très peu d'efforts quand on sait que le compilateur peut maintenant voir exactement ce qui se passe et vérifier votre code en conséquence.
Pour une discussion plus poussée à ce sujet, reportez-vous ici.
Ce groupe de problèmes reposant sur l'analogie suivante : dans un programme utilisant les génériques Java 5, tous les appels de bibliothèques antérieures à la version Java 5 peuvent exposer des types bruts, c'est-à-dire des applications d'un type générique qui ne parviennent pas à spécifier des arguments de type concrets. Pour adapter ces valeurs dans un programme à l'aide de génériques, le compilateur peut ajouter une conversion implicite en partant du principe que les arguments de type ont été spécifiés de la manière attendue par la partie client du code. Le compilateur générera un avertissement concernant l'utilisation de cette conversion et poursuivra la vérification des types en partant du principe que la bibliothèque "fonctionne correctement". De la même manière, un type de retour non annoté d'une méthode de bibliothèque peut être considéré comme un type "brut" ou "existant". Là aussi, une conversion implicite peut supposer de manière optimiste la spécification attendue. Là aussi, un avertissement est généré et l'analyse se poursuit en partant du principe que la bibliothèque "fonctionne correctement".
D'un point de vue théorique, le recours à ces conversions implicites indique une violation de spécification. Néanmoins, dans ce cas, il peut s'agir d'un code tiers qui viole la spécification qu'attend notre code. Ou alors, comme nous en sommes convaincus, peut-être que du code tiers remplit le contrat, mais ne parvient pas à effectuer la déclaration (car il n'utilise pas d'annotations null). Dans de telles situations, nous ne pourrions peut-être pas être capable de résoudre exactement le problème pour des raisons organisationnelles.
@SuppressWarnings("null")
@NonNull Foo foo = Library.getFoo(); // implicit conversion
foo.bar();
Le fragment de code ci-dessus suppose que la méthode Library.getFoo() renvoie un Foo
sans spécifier d'annotation null. Nous pouvons intégrer la valeur de retour dans notre programme annoté en affectant une variable locale @NonNull qui déclenche un avertissement concernant une conversion non contrôlée.
En ajoutant un élément SuppressWarnings("null") correspondant à cette déclaration, nous admettons le danger inhérent et prenons la responsabilité de vérifier que la bibliothèque se comporte réellement comme nous l'attendons.
Si l'analyse de flux ne peut pas voir qu'une valeur n'est pas null, la stratégie la plus simple consiste toujours à ajouter une nouvelle variable locale sectorisée annotée avec @NonNull.
Si vous êtes convaincu que la valeur affectée à cette variable locale ne sera jamais null à l'exécution, alors vous pouvez utiliser une méthode auxiliaire comme dans l'exemple suivant :
static @NonNull <T> T assertNonNull(@Nullable T value, @Nullable String msg) {
if (value == null) throw new AssertionError(msg);
return value;
}
@NonNull MyType foo() {
if (isInitialized()) {
MyType couldBeNull = getObjectOrNull();
@NonNull MyType theValue = assertNonNull(couldBeNull,
"value should not be null because application " +
"is fully initialized at this point.");
return theValue;
}
return new MyTypeImpl();
}
Notez qu'en utilisant la méthode assertNonNull() ci-dessus, vous endossez la responsabilité que cette supposition sera toujours valable à l'exécution.
Si ce n'est pas ce que vous voulez, les variables locales annotées vous aideront toujours à délimiter où et pourquoi l'analyse voit la possibilité qu'une valeur passe à un certain emplacement.
A l'heure où les outils de développement Java version 3.8.0 étaient publiés, la collecte de conseils concernant l'adoption d'annotations null est toujours en cours. C'est pourquoi ces informations sont actuellement gérées dans le wiki Eclipse.