diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 2a307cb55..11f89c4fb 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -94,6 +94,7 @@ The type attribute can be add,update,fix,remove. Add ObjectToStringComparator. #483. Add org.apache.commons.lang3.arch.Processor.Arch.getLabel(). Add IS_JAVA_14 and IS_JAVA_15 to org.apache.commons.lang3.SystemUtils. + ObjectUtils: Get first non-null supplier value. diff --git a/src/main/java/org/apache/commons/lang3/ObjectUtils.java b/src/main/java/org/apache/commons/lang3/ObjectUtils.java index 3b83ce16c..b647c1a4a 100644 --- a/src/main/java/org/apache/commons/lang3/ObjectUtils.java +++ b/src/main/java/org/apache/commons/lang3/ObjectUtils.java @@ -209,6 +209,45 @@ public class ObjectUtils { return null; } + /** + *

Executes the given suppliers in order and returns the first return + * value where a value other than {@code null} is returned. + * Once a non-{@code null} value is obtained, all following suppliers are + * not executed anymore. + * If all the return values are {@code null} or no suppliers are provided + * then {@code null} is returned.

+ * + *
+     * ObjectUtils.firstNonNullLazy(null, () -> null) = null
+     * ObjectUtils.firstNonNullLazy(() -> null, () -> "") = ""
+     * ObjectUtils.firstNonNullLazy(() -> "", () -> throw new IllegalStateException()) = ""
+     * ObjectUtils.firstNonNullLazy(() -> null, () -> "zz) = "zz"
+     * ObjectUtils.firstNonNullLazy() = null
+     * 
+ * + * @param the type of the return values + * @param suppliers the suppliers returning the values to test. + * {@code null} values are ignored. + * Suppliers may return {@code null} or a value of type @{code T} + * @return the first return value from {@code suppliers} which is not {@code null}, + * or {@code null} if there are no non-null values + * @since 3.10 + */ + @SafeVarargs + public static T getFirstNonNull(final Supplier... suppliers) { + if (suppliers != null) { + for (final Supplier supplier : suppliers) { + if (supplier != null) { + T value = supplier.get(); + if (value != null) { + return value; + } + } + } + } + return null; + } + /** *

* Returns the given {@code object} is it is non-null, otherwise returns the Supplier's {@link Supplier#get()} diff --git a/src/test/java/org/apache/commons/lang3/ObjectUtilsTest.java b/src/test/java/org/apache/commons/lang3/ObjectUtilsTest.java index 19d61103c..7fcdc4b71 100644 --- a/src/test/java/org/apache/commons/lang3/ObjectUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/ObjectUtilsTest.java @@ -25,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.lang.reflect.Constructor; @@ -149,6 +150,28 @@ public class ObjectUtilsTest { assertNull(ObjectUtils.firstNonNull((Object[]) null)); } + @Test + public void testGetFirstNonNull() { + // first non null + assertEquals("", ObjectUtils.getFirstNonNull(() -> null, () -> "")); + // first encountered value is used + assertEquals("1", ObjectUtils.getFirstNonNull(() -> null, () -> "1", () -> "2", () -> null)); + assertEquals("123", ObjectUtils.getFirstNonNull(() -> "123", () -> null, () -> "456")); + // don't evaluate suppliers after first value is found + assertEquals("123", ObjectUtils.getFirstNonNull(() -> null, () -> "123", () -> fail("Supplier after first non-null value should not be evaluated"))); + // supplier returning null and null supplier both result in null + assertNull(ObjectUtils.getFirstNonNull(null, () -> null)); + // Explicitly pass in an empty array of Object type to ensure compiler doesn't complain of unchecked generic array creation + assertNull(ObjectUtils.getFirstNonNull()); + // supplier is null + assertNull(ObjectUtils.getFirstNonNull((Supplier) null)); + // varargs array itself is null + assertNull(ObjectUtils.getFirstNonNull((Supplier[]) null)); + // test different types + assertEquals(1, ObjectUtils.getFirstNonNull(() -> null, () -> 1)); + assertEquals(Boolean.TRUE, ObjectUtils.getFirstNonNull(() -> null, () -> Boolean.TRUE)); + } + /** * Tests {@link ObjectUtils#anyNotNull(Object...)}. */