Je lisais un article lié à une histoire de slashdot et suis tombé sur cette petite friandise:
Prenez la dernière version de Java, qui essaie de faire la vérification null-pointeur plus facile en offrant une syntaxe abrégée pour les tests de pointeur sans fin. Juste ajout d'un point d'interrogation à chaque méthode L'invocation inclut automatiquement un teste les pointeurs nuls en remplaçant un le nid de rat des déclarations si-alors, telles que comme:
public String getPostcode(Person person) { String ans= null; if (person != null) { Name nm= person.getName(); if (nm!= null) { ans= nm.getPostcode(); } } return ans }Avec ça:
public String getFirstName(Person person) { return person?.getName()?.getGivenName(); }
J'ai fouillé sur Internet (d'accord, j'ai passé au moins 15 minutes à googler des variantes de "point d'interrogation Java") et je n'ai rien eu. Donc, ma question: existe-t-il une documentation officielle à ce sujet? J'ai trouvé que C # avait un opérateur similaire (l'opérateur "??"), mais j'aimerais obtenir la documentation de la langue dans laquelle je travaille. Ou s'agit-il simplement d'une utilisation de l'opérateur ternaire que j'ai déjà jamais vu avant.
Merci!
EDIT: Lien vers l'article: http://infoworld.com/d/developer-world/12-programming-mistakes-avoid-292
L'idée originale vient de groovy. Il a été proposé pour Java 7 dans le cadre de Project Coin: https://wiki.openjdk.Java.net/display/Coin/2009+Proposals+TOC (Elvis et autres opérateurs Null-Safe), mais n'a pas pas encore été accepté.
L'opérateur Elvis associé?: A été proposé de rendre x ?: y
abrégé pour x != null ? x : y
, ce qui est particulièrement utile lorsque x est une expression complexe.
Cette syntaxe n’existe pas en Java et elle n’est pas censée être incluse dans les versions à venir que je connaisse.
Il y avait une proposition pour cela dans Java 7, mais elle a été rejetée:
Une façon de contourner le manque de "?" L'opérateur qui utilise Java 8 sans la surcharge de try-catch (qui pourrait aussi masquer une NullPointerException
d'origine, comme mentionné) est de créer une classe pour "canaliser" les méthodes dans un style Java-8-Stream.
public class Pipe<T> {
private T object;
private Pipe(T t) {
object = t;
}
public static<T> Pipe<T> of(T t) {
return new Pipe<>(t);
}
public <S> Pipe<S> after(Function<? super T, ? extends S> plumber) {
return new Pipe<>(object == null ? null : plumber.apply(object));
}
public T get() {
return object;
}
public T orElse(T other) {
return object == null ? other : object;
}
}
Alors, l'exemple donné deviendrait:
public String getFirstName(Person person) {
return Pipe.of(person).after(Person::getName).after(Name::getGivenName).get();
}
[MODIFIER]
Après réflexion, je me suis rendu compte qu’il était effectivement possible d’obtenir la même chose en utilisant des classes Java 8 standard:
public String getFirstName(Person person) {
return Optional.ofNullable(person).map(Person::getName).map(Name::getGivenName).orElse(null);
}
Dans ce cas, il est même possible de choisir une valeur par défaut (telle que "<no first name>"
) au lieu de null
en la passant comme paramètre orElse
.
Voir: https://blogs.Oracle.com/darcy/project-coin:-the-final-five-or-so (en particulier "Elvis et autres opérateurs de sécurité null").
Le résultat est que cette fonctionnalité a été prise en compte pour Java 7, mais n'a pas été incluse.
C'est en fait L'opérateur de déréférence de Groovy . Vous ne pouvez pas l'utiliser en Java pur (malheureusement), de sorte que le message est tout simplement faux (ou plus probablement légèrement trompeur s'il prétend que Groovy est la "dernière version de Java").
Il est possible de définir des méthodes util qui résolvent ce problème de manière presque jolie avec Java 8 lambda.
Il s’agit d’une variante de la solution H-MAN mais elle utilise des méthodes surchargées avec plusieurs arguments pour gérer plusieurs étapes au lieu d’attraper NullPointerException
.
Même si je pense que cette solution est plutôt cool, je pense que je préfère celle de Helder Pereira secondes car elle ne nécessite aucune méthode utilitaire.
void example() {
Entry entry = new Entry();
// This is the same as H-MANs solution
Person person = getNullsafe(entry, e -> e.getPerson());
// Get object in several steps
String givenName = getNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.getGivenName());
// Call void methods
doNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.nameIt());
}
/** Return result of call to f1 with o1 if it is non-null, otherwise return null. */
public static <R, T1> R getNullsafe(T1 o1, Function<T1, R> f1) {
if (o1 != null) return f1.apply(o1);
return null;
}
public static <R, T0, T1> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, R> f2) {
return getNullsafe(getNullsafe(o0, f1), f2);
}
public static <R, T0, T1, T2> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Function<T2, R> f3) {
return getNullsafe(getNullsafe(o0, f1, f2), f3);
}
/** Call consumer f1 with o1 if it is non-null, otherwise do nothing. */
public static <T1> void doNullsafe(T1 o1, Consumer<T1> f1) {
if (o1 != null) f1.accept(o1);
}
public static <T0, T1> void doNullsafe(T0 o0, Function<T0, T1> f1, Consumer<T1> f2) {
doNullsafe(getNullsafe(o0, f1), f2);
}
public static <T0, T1, T2> void doNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Consumer<T2> f3) {
doNullsafe(getNullsafe(o0, f1, f2), f3);
}
class Entry {
Person getPerson() { return null; }
}
class Person {
Name getName() { return null; }
}
class Name {
void nameIt() {}
String getGivenName() { return null; }
}
Je ne suis pas sûr que cela fonctionnerait même; si, par exemple, la référence de la personne était nulle, avec quoi le runtime la remplacerait-elle? Une nouvelle personne? Cela nécessiterait que la personne ait une initialisation par défaut à laquelle vous vous attendez dans ce cas. Vous pouvez éviter les exceptions de référence null, mais vous obtiendrez tout de même un comportement imprévisible si vous n'aviez pas planifié ce type de configuration.
Le ?? L’opérateur en C # pourrait plutôt être appelé l’opérateur "coalesce"; vous pouvez chaîner plusieurs expressions et il retournera la première qui n'est pas nulle. Malheureusement, Java ne l'a pas. Je pense que le mieux que vous puissiez faire est d'utiliser l'opérateur ternaire pour effectuer des contrôles nuls et évaluer une alternative à l'expression entière si l'un des membres de la chaîne est nul:
return person == null ? ""
: person.getName() == null ? ""
: person.getName().getGivenName();
Vous pouvez également utiliser try-catch:
try
{
return person.getName().getGivenName();
}
catch(NullReferenceException)
{
return "";
}
Java n'a pas la syntaxe exact mais à partir de JDK-8, nous avons le API facultative avec différentes méthodes. Donc, la version C # avec l'utilisation de opérateur conditionnel null :
return person?.getName()?.getGivenName();
peut être écrit comme suit en Java avec API facultative :
return Optional.ofNullable(person)
.map(e -> e.getName())
.map(e -> e.getGivenName())
.orElse(null);
si l'un des person
, getName
ou getGivenName
est null, alors null est renvoyé.
Si quelqu'un recherche une alternative aux anciennes versions de Java, vous pouvez essayer celui que j'ai écrit:
/**
* Strong typed Lambda to return NULL or DEFAULT VALUES instead of runtime errors.
* if you override the defaultValue method, if the execution result was null it will be used in place
*
*
* Sample:
*
* It won't throw a NullPointerException but null.
* <pre>
* {@code
* new RuntimeExceptionHandlerLambda<String> () {
* @Override
* public String evaluate() {
* String x = null;
* return x.trim();
* }
* }.get();
* }
* <pre>
*
*
* @author Robson_Farias
*
*/
public abstract class RuntimeExceptionHandlerLambda<T> {
private T result;
private RuntimeException exception;
public abstract T evaluate();
public RuntimeException getException() {
return exception;
}
public boolean hasException() {
return exception != null;
}
public T defaultValue() {
return result;
}
public T get() {
try {
result = evaluate();
} catch (RuntimeException runtimeException) {
exception = runtimeException;
}
return result == null ? defaultValue() : result;
}
}
Étant donné que Android ne prend pas en charge les fonctions Lambda, sauf si votre système d'exploitation installé est> = 24, nous devons utiliser la réflexion.
// Example using doIt function with sample classes
public void Test() {
testEntry(new Entry(null));
testEntry(new Entry(new Person(new Name("Bob"))));
}
static void testEntry(Entry entry) {
doIt(doIt(doIt(entry, "getPerson"), "getName"), "getName");
}
// Helper to safely execute function
public static <T,R> R doIt(T obj, String methodName) {
try {
if (obj != null)
return (R)obj.getClass().getDeclaredMethod(methodName).invoke(obj);
} catch (Exception ignore) {
}
return null;
}
// Sample test classes
static class Entry {
Person person;
Entry(Person person) { this.person = person; }
Person getPerson() { return person; }
}
static class Person {
Name name;
Person(Name name) { this.name = name; }
Name getName() { return name; }
}
static class Name {
String name;
Name(String name) { this.name = name; }
String getName() {
System.out.print(" Name:" + name + " ");
return name;
}
}
}
Voilà, invocation null-safe dans Java 8:
public void someMethod() {
String userName = nullIfAbsent(new Order(), t -> t.getAccount().getUser()
.getName());
}
static <T, R> R nullIfAbsent(T t, Function<T, R> funct) {
try {
return funct.apply(t);
} catch (NullPointerException e) {
return null;
}
}
Vous pouvez tester le code que vous avez fourni et cela donnera une erreur de syntaxe. Donc, il n'est pas supporté en Java. Groovy le supporte et il a été proposé pour Java 7 (mais n’a jamais été inclus).
Cependant, vous pouvez utiliser les éléments facultatifs fournis dans Java 8. Cela peut vous aider à obtenir quelque chose sur une ligne similaire https://docs.Oracle.com/javase/8/docs/api/Java/util/ Facultatif.htmlhttp://www.Oracle.com/technetwork/articles/Java/java8-optional-2175753.html