diff --git a/src/main/java/org/apache/commons/lang3/function/FailableBiConsumer.java b/src/main/java/org/apache/commons/lang3/function/FailableBiConsumer.java index 5f56b06e9..6d5d99f5f 100644 --- a/src/main/java/org/apache/commons/lang3/function/FailableBiConsumer.java +++ b/src/main/java/org/apache/commons/lang3/function/FailableBiConsumer.java @@ -37,7 +37,7 @@ public interface FailableBiConsumer { /** * Returns The NOP singleton. - * + * * @param Consumed type 1. * @param Consumed type 2. * @param Thrown exception. @@ -62,9 +62,9 @@ public interface FailableBiConsumer { * @param after the operation to perform after this one. * @return a composed {@code FailableBiConsumer} like {@link BiConsumer#andThen(BiConsumer)}. * @throws E Thrown when a consumer fails. - * @throws NullPointerException if {@code after} is null + * @throws NullPointerException if {@code after} is null. */ - default FailableBiConsumer andThen(FailableBiConsumer after) throws E { + default FailableBiConsumer andThen(final FailableBiConsumer after) throws E { Objects.requireNonNull(after); return (t, u) -> { diff --git a/src/main/java/org/apache/commons/lang3/function/FailableBiFunction.java b/src/main/java/org/apache/commons/lang3/function/FailableBiFunction.java index d112be8e9..be0e4704b 100644 --- a/src/main/java/org/apache/commons/lang3/function/FailableBiFunction.java +++ b/src/main/java/org/apache/commons/lang3/function/FailableBiFunction.java @@ -17,7 +17,9 @@ package org.apache.commons.lang3.function; +import java.util.Objects; import java.util.function.BiFunction; +import java.util.function.Function; /** * A functional interface like {@link BiFunction} that declares a {@code Throwable}. @@ -31,6 +33,37 @@ import java.util.function.BiFunction; @FunctionalInterface public interface FailableBiFunction { + /** NOP singleton */ + @SuppressWarnings("rawtypes") + final FailableBiFunction NOP = (t, u) -> null; + + /** + * Returns The NOP singleton. + * + * @param Consumed type 1. + * @param Consumed type 2. + * @param Return type. + * @param Thrown exception. + * @return The NOP singleton. + */ + static FailableBiFunction nop() { + return NOP; + } + + /** + * Returns a composed {@code FailableBiFunction} that like {@link BiFunction#andThen(Function)}. + * + * @param the type of output of the {@code after} function, and of the composed function + * @param after the operation to perform after this one. + * @return a composed {@code FailableBiFunction} that like {@link BiFunction#andThen(Function)}. + * @throws E Thrown when a consumer fails. + * @throws NullPointerException if after is null. + */ + default FailableBiFunction andThen(final FailableFunction after) throws E { + Objects.requireNonNull(after); + return (final T t, final U u) -> after.apply(apply(t, u)); + } + /** * Applies this function. * 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 7de443796..3d4b905b6 100644 --- a/src/main/java/org/apache/commons/lang3/function/FailableFunction.java +++ b/src/main/java/org/apache/commons/lang3/function/FailableFunction.java @@ -30,6 +30,22 @@ import java.util.function.Function; @FunctionalInterface public interface FailableFunction { + /** NOP singleton */ + @SuppressWarnings("rawtypes") + final FailableFunction NOP = t -> null; + + /** + * Returns The NOP singleton. + * + * @param Consumed type 1. + * @param Return type. + * @param Thrown exception. + * @return The NOP singleton. + */ + static FailableFunction nop() { + return NOP; + } + /** * Applies this function. * 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 056072594..81da56e11 100644 --- a/src/test/java/org/apache/commons/lang3/function/FailableFunctionsTest.java +++ b/src/test/java/org/apache/commons/lang3/function/FailableFunctionsTest.java @@ -45,9 +45,6 @@ import org.junit.jupiter.api.Test; */ public class FailableFunctionsTest { - private static final IllegalStateException ILLEGAL_STATE_EXCEPTION = new IllegalStateException(); - private static final OutOfMemoryError ERROR = new OutOfMemoryError(); - public static class CloseableObject { private boolean closed; @@ -69,7 +66,6 @@ public class FailableFunctionsTest { } } } - public static class FailureOnOddInvocations { private static int invocations; @@ -268,10 +264,15 @@ public class FailableFunctionsTest { } } + private static final OutOfMemoryError ERROR = new OutOfMemoryError(); + + private static final IllegalStateException ILLEGAL_STATE_EXCEPTION = new IllegalStateException(); + @Test void testAcceptBiConsumer() { final Testable testable = new Testable<>(null); - Throwable e = assertThrows(IllegalStateException.class, () -> Failable.accept(Testable::test, testable, ILLEGAL_STATE_EXCEPTION)); + Throwable e = assertThrows(IllegalStateException.class, + () -> Failable.accept(Testable::test, testable, ILLEGAL_STATE_EXCEPTION)); assertSame(ILLEGAL_STATE_EXCEPTION, e); e = assertThrows(OutOfMemoryError.class, () -> Failable.accept(Testable::test, testable, ERROR)); @@ -632,28 +633,27 @@ public class FailableFunctionsTest { t.setThrowable(th); t.test(); }; - Throwable e; final FailableBiConsumer, Throwable, Throwable> nop = FailableBiConsumer.nop(); - e = assertThrows(OutOfMemoryError.class, () -> nop.andThen(failableBiConsumer).accept(testable, ERROR)); + final Throwable e = assertThrows(OutOfMemoryError.class, + () -> nop.andThen(failableBiConsumer).accept(testable, ERROR)); assertSame(ERROR, e); // Does not throw nop.andThen(nop); // Documented in Javadoc edge-case. assertThrows(NullPointerException.class, () -> failableBiConsumer.andThen(null)); - } @Test public void testBiFunction() { - final IllegalStateException ise = ILLEGAL_STATE_EXCEPTION; - final Testable testable = new Testable<>(ise); + final Testable testable = new Testable<>(ILLEGAL_STATE_EXCEPTION); final FailableBiFunction, Throwable, Integer, Throwable> failableBiFunction = (t, th) -> { t.setThrowable(th); - return Integer.valueOf(t.testAsInteger()); + return t.testAsInteger(); }; final BiFunction, Throwable, Integer> biFunction = Failable.asBiFunction(failableBiFunction); - Throwable e = assertThrows(IllegalStateException.class, () -> biFunction.apply(testable, ise)); - assertSame(ise, e); + Throwable e = assertThrows(IllegalStateException.class, + () -> biFunction.apply(testable, ILLEGAL_STATE_EXCEPTION)); + assertSame(ILLEGAL_STATE_EXCEPTION, e); testable.setThrowable(ERROR); e = assertThrows(OutOfMemoryError.class, () -> biFunction.apply(testable, ERROR)); @@ -669,6 +669,29 @@ public class FailableFunctionsTest { assertEquals(0, biFunction.apply(testable, null).intValue()); } + @Test + public void testBiFunctionAndThen() throws IOException { + // Unchecked usage pattern in JRE + final BiFunction nopBiFunction = (t, u) -> null; + final Function nopFunction = (t) -> null; + nopBiFunction.andThen(nopFunction); + // Checked usage pattern + final FailableBiFunction failingBiFunctionTest = (t, u) -> { + throw new IOException(); + }; + final FailableFunction failingFunction = (t) -> { throw new IOException(); }; + final FailableBiFunction nopFailableBiFunction = FailableBiFunction.nop(); + final FailableFunction nopFailableFunction = FailableFunction.nop(); + // + assertThrows(IOException.class, () -> failingBiFunctionTest.andThen(failingFunction).apply(null, null)); + assertThrows(IOException.class, () -> failingBiFunctionTest.andThen(nopFailableFunction).apply(null, null)); + // + assertThrows(IOException.class, () -> nopFailableBiFunction.andThen(failingFunction).apply(null, null)); + nopFailableBiFunction.andThen(nopFailableFunction).apply(null, null); + // Documented in Javadoc edge-case. + assertThrows(NullPointerException.class, () -> failingBiFunctionTest.andThen(null)); + } + @Test @DisplayName("Test that asPredicate(FailableBiPredicate) is converted to -> BiPredicate ") public void testBiPredicate() {