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


есть ли Java-эквивалент SQL

12 118

12 ответов:

нет, нет.

самое близкое, что вы можете получить:

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

Если есть только две переменные для проверки, и вы используете Guava, вы можете использовать MoreObjects.firstNonNull (T первый, t второй).

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

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

Если вы импортируете статический необязательный выражение не слишком плохо.

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

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

следуя ответу 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);
}

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

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

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 – (написано на чешском языке, но я надеюсь, что примеры кода понятным для всех).

С гуавой вы можете сделать:

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

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

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

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

только для полноты, случай "нескольких переменных" действительно возможен, хотя и не элегантен вообще. Например, для переменных 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>>).

вы можете попробовать это:

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

на основе этой ответ

как насчет использования поставщиков, когда вы хотите, чтобы избежать оценки какой-то дорогой метод?

такой:

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

а затем использовать его так:

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

вы также можете использовать перегруженные методы для вызовов с двумя, тремя или четырьмя аргументами.

кроме того, вы также можете использовать потоки с чем-то вроде этого:

public static <T> T coalesce2(Supplier<T>... s) {
    return Arrays.stream(s).map(Supplier::get).filter(Objects::nonNull).findFirst().orElse(null);
}
Object coalesce(Object... objects)
{
    for(Object o : object)
        if(o != null)
            return o;
    return null;
}

Как насчет:

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

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