Как получить первое ненулевое значение в Java?

Существует ли Java-эквивалент функции SQL COALESCE? То есть, есть ли способ вернуть первое ненулевое значение нескольких переменных?

например.

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

Я хочу как-то получить инструкцию, которая вернет первое ненулевое значение a, b и c - в этом случае оно вернет b или 4.4. (Что-то вроде метода sql - return COALESCE(a,b,c)). Я знаю, что могу сделать это явно с чем-то вроде:

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

Но я подумал, есть ли какая-нибудь встроенная, принятая функция для выполнения этого.

Ответ 1

Нет, нет.

Ближе всего вы можете получить:

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

По соображениям эффективности вы можете обрабатывать общие случаи следующим образом:

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

Ответ 4

Если для тестирования есть только две ссылки, и вы используете Java 8, вы можете использовать

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

Если вы импортируете static Optional, выражение не так уж плохо.

К сожалению, ваш случай с "несколькими переменными" невозможен с помощью необязательного метода. Вместо этого вы можете использовать:

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

Ответ 5

После ответа LES2 вы можете устранить некоторое повторение в эффективной версии, вызвав перегруженную функцию:

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

Ответ 6

Эта ситуация требует некоторого препроцессора. Потому что, если вы пишете функцию (статический метод), которая выбирает первое не пустое значение, оно оценивает все элементы. Проблема заключается в том, что некоторые элементы являются вызовами метода (могут быть дорогостоящие вызовы метода). И эти методы вызывают, даже если какой-либо элемент перед ними не является нулевым.

Некоторые функции, такие как

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

но перед компиляцией в байтовый код должен быть препроцессор, который находит использование этой "функции коалесценции" и заменяет его конструкцией типа

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

Обновление 2014-09-02:

Благодаря Java 8 и Lambdas есть возможность иметь истинную совместную работу на Java! Включение ключевой функции: конкретные выражения оцениваются только тогда, когда это необходимо - если раньше один не является нулевым, то следующие не оцениваются (методы не вызывают, вычисление или операции с дисками/сетями не выполняются).

Я написал статью об этом Java 8: coalesce - hledáme neNULLové hodnoty - (написан на чешском языке, но я надеюсь, что примеры кода понятны для всех).

Ответ 7

С помощью Guava вы можете:

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

который не бросает NPE, если оба a и b равны null.

EDIT: я ошибался, это бросает NPE. Правильный способ, который прокомментировал Михал Čizmazia:

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

Ответ 8

Просто для полноты, случай с несколькими переменными действительно возможен, хотя и не изящный. Например, для переменных o, p и q:

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

Обратите внимание, что использование orElseGet() для случая, когда o, p и q не являются переменными, а выражениями либо дорогими, либо нежелательными побочными эффектами.

В самом общем случае 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

Это может генерировать выражения чрезмерно долго. Однако, если мы пытаемся перейти в мир без null, то v[i] скорее всего уже имеет тип Optional<String>, а не просто String. В этом случае

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

или в случае выражений:

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

Кроме того, если вы также переходите к функционально-декларативному стилю, o, p и q должны иметь тип Supplier<String>, как в:

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

И тогда весь coalesce просто сводится к o.get().

Для более конкретного примера:

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(), а ageFromInput(), естественно, вернет Optional<Integer>.

И тогда coalesce становится effectiveAge.get() или просто effectiveAge, если мы довольны Supplier<Integer>.

IMHO, с Java 8 мы увидим все больше и больше кода, структурированного таким образом, так как это чрезвычайно самоочевидно и эффективно в одно и то же время, особенно в более сложных случаях.

Я пропускаю класс Lazy<T>, который вызывает Supplier<T> только один раз, но лениво, а также согласованность в определении Optional<T> (т.е. Optional<T> - Optional<T> операторов или даже Supplier<Optional<T>>).

Ответ 9

Object coalesce(Object... objects)
{
    for(Object o : object)
        if(o != null)
            return o;
    return null;
}

Ответ 10

Как насчет:

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

Java ArrayList удобно разрешает пустые записи, и это выражение согласовано независимо от количества объектов, которые необходимо учитывать. (В этом виде все рассмотренные объекты должны быть одного типа.)