diff --git a/src/main/java/org/apache/commons/lang3/function/FailableFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableFunction.java index cb100a4b2..49af31c39 100644 --- a/src/main/java/org/apache/commons/lang3/function/FailableFunction.java +++ b/src/main/java/org/apache/commons/lang3/function/FailableFunction.java @@ -35,6 +35,17 @@ public interface FailableFunction { @SuppressWarnings("rawtypes") FailableFunction NOP = t -> null; + /** + * Returns a function that always returns its input argument. + * + * @param the type of the input and output objects to the function + * @param Thrown exception. + * @return a function that always returns its input argument + */ + static FailableFunction identity() { + return t -> t; + } + /** * Returns The NOP singleton. * @@ -70,4 +81,18 @@ public interface FailableFunction { */ R apply(T input) throws E; + /** + * Returns a composed {@code FailableFunction} like {@link Function#compose(Function)}. + * + * @param the input type to the {@code before} function, and to the composed function. + * @param before the operator to apply before this one. + * @return a a composed {@code FailableFunction} like {@link Function#compose(Function)}. + * @throws NullPointerException if before is null. + * @throws E Thrown when a consumer fails. + * @see #andThen(FailableFunction) + */ + default FailableFunction compose(final FailableFunction before) throws E { + Objects.requireNonNull(before); + return (final V v) -> apply(before.apply(v)); + } } diff --git a/src/test/java/org/apache/commons/lang3/function/FailableFunctionsTest.java b/src/test/java/org/apache/commons/lang3/function/FailableFunctionsTest.java index ce8b56759..ca3ccd4a6 100644 --- a/src/test/java/org/apache/commons/lang3/function/FailableFunctionsTest.java +++ b/src/test/java/org/apache/commons/lang3/function/FailableFunctionsTest.java @@ -859,6 +859,34 @@ public class FailableFunctionsTest { assertThrows(NullPointerException.class, () -> failingFunction.andThen(null)); } + @Test + public void testFunctionCompose() throws Throwable { + final Testable testable = new Testable<>(null); + final FailableFunction failing = t -> { + testable.setThrowable(ERROR); + testable.test(); + return 0; + }; + final FailableFunction nop = FailableFunction.nop(); + Throwable e = assertThrows(OutOfMemoryError.class, () -> nop.compose(failing).apply(0)); + assertSame(ERROR, e); + e = assertThrows(OutOfMemoryError.class, () -> failing.compose(nop).apply(0)); + assertSame(ERROR, e); + // Does not throw + nop.compose(nop); + // Documented in Javadoc edge-case. + assertThrows(NullPointerException.class, () -> failing.compose(null)); + } + + @Test + public void testFunctionIdentity() throws Throwable { + final FailableFunction nop = FailableFunction.identity(); + // Does not throw + nop.compose(nop); + // Documented in Javadoc edge-case. + assertThrows(NullPointerException.class, () -> nop.compose(null)); + } + @Test public void testGetAsBooleanSupplier() { final Testable testable = new Testable<>(ILLEGAL_STATE_EXCEPTION);