Как получить первое ненулевое значение в 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 ...
}
Ответ 2
ObjectUtils.firstNonNull(T...)
, из Apache Commons Lang 3, звучит как решение этой проблемы.
Ответ 3
Если вы используете Guava, вы можете использовать MoreObjects.firstNonNull(T...).
Ответ 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 удобно разрешает пустые записи, и это выражение согласовано независимо от количества объектов, которые необходимо учитывать. (В этом виде все рассмотренные объекты должны быть одного типа.)