web-dev-qa-db-fra.com

Java "?" Opérateur de vérification null - De quoi s'agit-il? (Pas ternaire!)

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

66
Erty Seidohl

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.

66
ataylor

Cette syntaxe n’existe pas en Java et elle n’est pas censée être incluse dans les versions à venir que je connaisse.

51
ColinD

Il y avait une proposition pour cela dans Java 7, mais elle a été rejetée:

http://tech.puredanger.com/Java7/#null

16
PaulJWilliams

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.

14
Helder Pereira

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.

7
Darron

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").

6
Andrzej Doyle

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; }
}
2
Lii

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 "";
}
1
KeithS

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é.

0
Aomine

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;
    }

}
0
irobson

É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;
        }
    }
}
0
LanDenLabs

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;
    }
}
0
H-MAN

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

Exemple de code pour facultatif

0
SK -