Как получить первое ненулевое значение в Java?
есть ли Java-эквивалент SQL
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 ... }
Apache Commons Lang 3
ObjectUtils.firstNonNull(T...)
Java 8 Stream
Stream.of(T...).filter(Objects::nonNull).findFirst().orElse(null)
Если есть только две переменные для проверки, и вы используете 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
arenull
.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); }
Как насчет:
firstNonNull = FluentIterable.from( Lists.newArrayList( a, b, c, ... ) ) .firstMatch( Predicates.notNull() ) .or( someKnownNonNullDefault );
Java ArrayList удобно разрешает нулевые записи, и это выражение согласовано независимо от количества объектов, которые будут рассмотрены. (В этой форме все рассматриваемые объекты должны быть одного типа.)