web-dev-qa-db-fra.com

Comment obtenir la première valeur non nulle en Java?

Existe-t-il un équivalent Java de la fonction COALESCE de SQL? Autrement dit, existe-t-il un moyen de renvoyer la première valeur non nulle de plusieurs variables?

par exemple.

Double a = null;
Double b = 4.4;
Double c = null;

Je veux d'une manière ou d'une autre une déclaration qui renverra la première valeur non nulle de a, b et c - dans ce cas, elle renverrait b ou 4.4. (Quelque chose comme la méthode SQL - return COALESCE(a,b,c)). Je sais que je peux le faire explicitement avec quelque chose comme:

return a != null ? a : (b != null ? b : c)

Mais je me suis demandé s'il existait une fonction intégrée et acceptée pour accomplir cela.

124
froadie

Non, il n'y en a pas.

Le plus proche que vous pouvez obtenir est:

public static <T> T coalesce(T ...items) {
    for(T i : items) if(i != null) return i;
    return null;
}

Pour des raisons d'efficacité, vous pouvez gérer les cas courants comme suit:

public static <T> T coalesce(T a, T b) {
    return a == null ? b : a;
}
public static <T> T coalesce(T a, T b, T c) {
    return a != null ? a : (b != null ? b : c);
}
public static <T> T coalesce(T a, T b, T c, T d) {
    return ...
}
96
les2
139
Andrzej Polis

S'il n'y a que deux variables à vérifier et que vous utilisez Guava, vous pouvez utiliser MoreObjects.firstNonNull (T en premier, T en second) .

52
Dave

S'il n'y a que deux références à tester et que vous utilisez Java 8, vous pouvez utiliser

Object o = null;
Object p = "p";
Object r = Optional.ofNullable( o ).orElse( p );
System.out.println( r );   // p

Si vous importez static facultatif, l'expression n'est pas trop mauvaise.

Malheureusement, votre cas avec "plusieurs variables" n'est pas possible avec une méthode optionnelle. Au lieu de cela, vous pouvez utiliser:

Object o = null;
Object p = null;
Object q = "p";

Optional<Object> r = Stream.of( o, p, q ).filter( Objects::nonNull ).findFirst();
System.out.println( r.orElse(null) );   // p
35
Christian Ullenboom

Suite à la réponse de LES2, vous pouvez éliminer certaines répétitions dans la version efficace en appelant la fonction surchargée:

public static <T> T coalesce(T a, T b) {
    return a != null ? a : b;
}
public static <T> T coalesce(T a, T b, T c) {
    return a != null ? a : coalesce(b,c);
}
public static <T> T coalesce(T a, T b, T c, T d) {
    return a != null ? a : coalesce(b,c,d);
}
public static <T> T coalesce(T a, T b, T c, T d, T e) {
    return a != null ? a : coalesce(b,c,d,e);
}
24
Eric

Cette situation appelle un préprocesseur. En effet, si vous écrivez une fonction (méthode statique) qui sélectionne la première valeur non nulle, elle évalue tous les éléments. C'est un problème si certains éléments sont des appels de méthode (il peut s'agir d'appels de méthode coûteux en temps). Et ces méthodes sont appelées même si aucun des éléments les précédant n’est null.

Certains fonctionnent comme ça

public static <T> T coalesce(T ...items) …

devrait être utilisé, mais avant de compiler en code octet, il devrait y avoir un pré-processeur qui trouve les utilisations de cette "fonction de coalesce" et le remplace par une construction telle que

a != null ? a : (b != null ? b : c)

Mise à jour 2014-09-02:

Grâce à Java 8 et Lambdas, il est possible d'avoir une vraie fusion en Java! Incluant la fonctionnalité cruciale: des expressions particulières sont évaluées uniquement lorsque cela est nécessaire - si l'une des expressions précédentes n'est pas nulle, les suivantes ne sont pas évaluées (les méthodes ne sont pas appelées, les opérations de calcul ou les opérations disque/réseau ne sont pas effectuées).

J'ai écrit un article à ce sujet Java 8: coalesce - hledáme neNULLové hodnoty - (écrit en tchèque, mais j'espère que les exemples de code sont compréhensibles pour tout le monde).

7
Franta

Avec Guava, vous pouvez faire:

Optional.fromNullable(a).or(b);

qui ne jette pas NPE si a et b sont null.

EDIT: Je me suis trompé, il jette NPE. La manière correcte, commentée par Michal Čizmazia est:

Optional.fromNullable(a).or(Optional.fromNullable(b)).orNull();
5
Jamol

Juste pour compléter, le cas des "plusieurs variables" est en effet possible, bien que pas élégant du tout. Par exemple, pour les variables o, p et q:

Optional.ofNullable( o ).orElseGet(()-> Optional.ofNullable( p ).orElseGet(()-> q ) )

Veuillez noter l'utilisation de orElseGet() dans le cas où o, p et q ne sont pas des variables mais des expressions coûteuses ou avec des effets secondaires indésirables.

Dans le cas le plus général coalesce(e[1],e[2],e[3],...,e[N])

coalesce-expression(i) ==  e[i]  when i = N
coalesce-expression(i) ==  Optional.ofNullable( e[i] ).orElseGet(()-> coalesce-expression(i+1) )  when i < N

Cela peut générer des expressions excessivement longues. Cependant, si nous essayons de passer à un monde sans null, alors v[i] sera probablement déjà du type Optional<String>, par opposition à simplement String. Dans ce cas, 

result= o.orElse(p.orElse(q.get())) ;

ou dans le cas d'expressions:

result= o.orElseGet(()-> p.orElseGet(()-> q.get() ) ) ;

De plus, si vous passez également à un style déclaratif fonctionnel, o, p et q doivent être de type Supplier<String> comme dans:

Supplier<String> q= ()-> q-expr ;
Supplier<String> p= ()-> Optional.ofNullable(p-expr).orElseGet( q ) ;
Supplier<String> o= ()-> Optional.ofNullable(o-expr).orElseGet( p ) ;

Et ensuite, la totalité de coalesce se réduit simplement à o.get().

Pour un exemple plus concret:

Supplier<Integer> hardcodedDefaultAge= ()-> 99 ;
Supplier<Integer> defaultAge= ()-> defaultAgeFromDatabase().orElseGet( hardcodedDefaultAge ) ;
Supplier<Integer> ageInStore= ()-> ageFromDatabase(memberId).orElseGet( defaultAge ) ;
Supplier<Integer> effectiveAge= ()-> ageFromInput().orElseGet( ageInStore ) ;

defaultAgeFromDatabase(), ageFromDatabase() et ageFromInput() renverraient déjà Optional<Integer>, naturellement.

Et puis la coalesce devient effectiveAge.get() ou simplement effectiveAge si nous sommes satisfaits d'un Supplier<Integer>.

IMHO, avec Java 8, nous verrons de plus en plus de code structuré comme celui-ci, car il est extrêmement explicite et efficace à la fois, en particulier dans les cas les plus complexes.

Il me manque une classe Lazy<T> qui appelle un Supplier<T> une seule fois, mais paresseusement, ainsi que la cohérence dans la définition de Optional<T> (c'est-à-dire Optional<T>-Optional<T> opérateurs, ou même Supplier<Optional<T>>).

4
Mario Rossi

Vous pouvez essayer ceci:

public static <T> T coalesce(T... t) {
    return Stream.of(t).filter(Objects::nonNull).findFirst().orElse(null);
}

Basé sur ce réponse

3
Lucas León

Pourquoi ne pas utiliser des fournisseurs pour éviter d’évaluer une méthode coûteuse? 

Comme ça:

public static <T> T coalesce(Supplier<T>... items) {
for (Supplier<T> item : items) {
    T value = item.get();
    if (value != null) {
        return value;
    }
    return null;
}

Et puis l'utiliser comme ça:

Double amount = coalesce(order::firstAmount, order::secondAmount, order::thirdAmount)

Vous pouvez également utiliser des méthodes surchargées pour les appels avec deux, trois ou quatre arguments.

En outre, vous pouvez également utiliser des flux avec quelque chose comme:

public static <T> T coalesce2(Supplier<T>... s) {
    return Arrays.stream(s).map(Supplier::get).filter(Objects::nonNull).findFirst().orElse(null);
}
1
Triqui

Que diriez-vous:

firstNonNull = FluentIterable.from(
    Lists.newArrayList( a, b, c, ... ) )
        .firstMatch( Predicates.notNull() )
            .or( someKnownNonNullDefault );

Java ArrayList autorise commodément les entrées nulles et cette expression est cohérente quel que soit le nombre d'objets à prendre en compte. (Sous cette forme, tous les objets considérés doivent être du même type.)

0
Lonnie