diff --git a/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingBiConsumer.java b/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingBiConsumer.java
new file mode 100644
index 0000000..b791a02
--- /dev/null
+++ b/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingBiConsumer.java
@@ -0,0 +1,29 @@
+package org.zeplinko.commons.lang.ext.util.function;
+
+/**
+ * A {@link java.util.function.BiConsumer}-like functional interface whose
+ * {@link #accept(Object, Object)} method is allowed to throw a checked
+ * {@link Exception}.
+ *
+ *
+ * This is useful when you want to use lambdas or method references that can
+ * throw checked exceptions in contexts where a {@code BiConsumer} would
+ * normally be used.
+ *
+ *
+ * @author Shivam Nagpal
+ *
+ * @param the type of the first argument to the operation
+ * @param the type of the second argument to the operation
+ */
+@FunctionalInterface
+public interface ThrowingBiConsumer {
+ /**
+ * Performs this operation on the given arguments.
+ *
+ * @param t the first input argument
+ * @param u the second input argument
+ * @throws Exception if the operation fails
+ */
+ void accept(T t, U u) throws Exception;
+}
diff --git a/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingBiFunction.java b/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingBiFunction.java
new file mode 100644
index 0000000..8c87600
--- /dev/null
+++ b/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingBiFunction.java
@@ -0,0 +1,31 @@
+package org.zeplinko.commons.lang.ext.util.function;
+
+/**
+ * A {@link java.util.function.BiFunction}-like functional interface whose
+ * {@link #apply(Object, Object)} method is allowed to throw a checked
+ * {@link Exception}.
+ *
+ *
+ * This is useful when you want to use lambdas or method references that can
+ * throw checked exceptions in contexts where a {@code BiFunction} would
+ * normally be used.
+ *
+ *
+ * @author Shivam Nagpal
+ *
+ * @param the type of the first argument to the function
+ * @param the type of the second argument to the function
+ * @param the type of the result of the function
+ */
+@FunctionalInterface
+public interface ThrowingBiFunction {
+ /**
+ * Applies this function to the given arguments.
+ *
+ * @param t the first function argument
+ * @param u the second function argument
+ * @return the function result
+ * @throws Exception if the operation fails
+ */
+ R apply(T t, U u) throws Exception;
+}
diff --git a/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingConsumer.java b/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingConsumer.java
new file mode 100644
index 0000000..4b08b27
--- /dev/null
+++ b/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingConsumer.java
@@ -0,0 +1,27 @@
+package org.zeplinko.commons.lang.ext.util.function;
+
+/**
+ * A {@link java.util.function.Consumer}-like functional interface whose
+ * {@link #accept(Object)} method is allowed to throw a checked
+ * {@link Exception}.
+ *
+ *
+ * This is useful when you want to use lambdas or method references that can
+ * throw checked exceptions in contexts where a {@code Consumer} would normally
+ * be used.
+ *
+ *
+ * @author Shivam Nagpal
+ *
+ * @param the type of the input to the operation
+ */
+@FunctionalInterface
+public interface ThrowingConsumer {
+ /**
+ * Performs this operation on the given argument.
+ *
+ * @param t the input argument
+ * @throws Exception if the operation fails
+ */
+ void accept(T t) throws Exception;
+}
diff --git a/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingFunction.java b/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingFunction.java
new file mode 100644
index 0000000..3b92a13
--- /dev/null
+++ b/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingFunction.java
@@ -0,0 +1,29 @@
+package org.zeplinko.commons.lang.ext.util.function;
+
+/**
+ * A {@link java.util.function.Function}-like functional interface whose
+ * {@link #apply(Object)} method is allowed to throw a checked
+ * {@link Exception}.
+ *
+ *
+ * This is useful when you want to use lambdas or method references that can
+ * throw checked exceptions in contexts where a {@code Function} would normally
+ * be used.
+ *
+ *
+ * @author Shivam Nagpal
+ *
+ * @param the type of the input to the function
+ * @param the type of the result of the function
+ */
+@FunctionalInterface
+public interface ThrowingFunction {
+ /**
+ * Applies this function to the given argument.
+ *
+ * @param t the function argument
+ * @return the function result
+ * @throws Exception if the operation fails
+ */
+ R apply(T t) throws Exception;
+}
diff --git a/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingRunnable.java b/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingRunnable.java
new file mode 100644
index 0000000..5abefb9
--- /dev/null
+++ b/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingRunnable.java
@@ -0,0 +1,24 @@
+package org.zeplinko.commons.lang.ext.util.function;
+
+/**
+ * A {@link java.lang.Runnable}-like functional interface whose {@link #run()}
+ * method is allowed to throw a checked {@link Exception}.
+ *
+ *
+ * This is useful when you want to use lambdas or method references that can
+ * throw checked exceptions in contexts where a {@code Runnable} would normally
+ * be used.
+ *
+ *
+ * @author Shivam Nagpal
+ *
+ */
+@FunctionalInterface
+public interface ThrowingRunnable {
+ /**
+ * Executes this runnable operation.
+ *
+ * @throws Exception if the operation fails
+ */
+ void run() throws Exception;
+}
diff --git a/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingSupplier.java b/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingSupplier.java
new file mode 100644
index 0000000..e8888e6
--- /dev/null
+++ b/src/main/java/org/zeplinko/commons/lang/ext/util/function/ThrowingSupplier.java
@@ -0,0 +1,26 @@
+package org.zeplinko.commons.lang.ext.util.function;
+
+/**
+ * A {@link java.util.function.Supplier}-like functional interface whose
+ * {@link #get()} method is allowed to throw a checked {@link Exception}.
+ *
+ *
+ * This is useful when you want to use lambdas or method references that can
+ * throw checked exceptions in contexts where a {@code Supplier} would normally
+ * be used.
+ *
+ *
+ * @author Shivam Nagpal
+ *
+ * @param the type of results supplied by this supplier
+ */
+@FunctionalInterface
+public interface ThrowingSupplier {
+ /**
+ * Gets a result.
+ *
+ * @return a result
+ * @throws Exception if the operation fails
+ */
+ T get() throws Exception;
+}
diff --git a/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingBiConsumerTest.java b/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingBiConsumerTest.java
new file mode 100644
index 0000000..dc99cae
--- /dev/null
+++ b/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingBiConsumerTest.java
@@ -0,0 +1,54 @@
+package org.zeplinko.commons.lang.ext.util.function;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ThrowingBiConsumerTest {
+
+ @Test
+ void test_whenAcceptIsInvoked_thenConsumesBothValues() throws Exception {
+ Map map = new HashMap<>();
+ ThrowingBiConsumer consumer = map::put;
+ consumer.accept("key", 42);
+ assertEquals(1, map.size());
+ assertEquals(42, map.get("key"));
+ }
+
+ @Test
+ void test_whenAcceptIsInvokedWithNullValues_thenConsumesNulls() throws Exception {
+ Map map = new HashMap<>();
+ ThrowingBiConsumer consumer = map::put;
+ consumer.accept(null, null);
+ assertEquals(1, map.size());
+ assertTrue(map.containsKey(null));
+ assertNull(map.get(null));
+ }
+
+ @Test
+ void test_whenAcceptThrowsException_thenExceptionIsPropagated() {
+ ThrowingBiConsumer consumer = (key, value) -> {
+ throw new IOException("Test exception");
+ };
+ Exception exception = assertThrows(Exception.class, () -> consumer.accept("key", 42));
+ assertEquals("Test exception", exception.getMessage());
+ assertInstanceOf(IOException.class, exception);
+ }
+
+ @Test
+ void test_whenAcceptIsInvokedMultipleTimes_thenConsumesAllValues() throws Exception {
+ Map map = new HashMap<>();
+ ThrowingBiConsumer consumer = map::put;
+ consumer.accept("one", 1);
+ consumer.accept("two", 2);
+ consumer.accept("three", 3);
+ assertEquals(3, map.size());
+ assertEquals(1, map.get("one"));
+ assertEquals(2, map.get("two"));
+ assertEquals(3, map.get("three"));
+ }
+}
diff --git a/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingBiFunctionTest.java b/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingBiFunctionTest.java
new file mode 100644
index 0000000..084913a
--- /dev/null
+++ b/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingBiFunctionTest.java
@@ -0,0 +1,58 @@
+package org.zeplinko.commons.lang.ext.util.function;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ThrowingBiFunctionTest {
+
+ @Test
+ void test_whenApplyIsInvoked_thenCombinesBothValues() throws Exception {
+ ThrowingBiFunction function = Integer::sum;
+ Integer result = function.apply(5, 3);
+ assertEquals(8, result);
+ }
+
+ @Test
+ void test_whenApplyIsInvokedWithNullValues_thenHandlesNulls() throws Exception {
+ ThrowingBiFunction function = (first, second) -> {
+ if (first == null && second == null)
+ return "both null";
+ if (first == null)
+ return "first null";
+ if (second == null)
+ return "second null";
+ return first + second;
+ };
+ assertEquals("both null", function.apply(null, null));
+ assertEquals("first null", function.apply(null, "test"));
+ assertEquals("second null", function.apply("test", null));
+ }
+
+ @Test
+ void test_whenApplyThrowsException_thenExceptionIsPropagated() {
+ ThrowingBiFunction function = (first, second) -> {
+ throw new IOException("Test exception");
+ };
+ Exception exception = assertThrows(Exception.class, () -> function.apply("a", "b"));
+ assertEquals("Test exception", exception.getMessage());
+ assertInstanceOf(IOException.class, exception);
+ }
+
+ @Test
+ void test_whenApplyIsInvokedMultipleTimes_thenTransformsEachPair() throws Exception {
+ ThrowingBiFunction function = (a, b) -> a * b;
+ assertEquals(6, function.apply(2, 3));
+ assertEquals(20, function.apply(4, 5));
+ assertEquals(42, function.apply(6, 7));
+ }
+
+ @Test
+ void test_whenApplyReturnsNull_thenReturnsNull() throws Exception {
+ ThrowingBiFunction function = (first, second) -> null;
+ String result = function.apply("a", "b");
+ assertNull(result);
+ }
+}
diff --git a/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingConsumerTest.java b/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingConsumerTest.java
new file mode 100644
index 0000000..bac4843
--- /dev/null
+++ b/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingConsumerTest.java
@@ -0,0 +1,63 @@
+package org.zeplinko.commons.lang.ext.util.function;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ThrowingConsumerTest {
+
+ @Test
+ void test_whenAcceptIsInvoked_thenConsumesValue() throws Exception {
+ List list = new ArrayList<>();
+ ThrowingConsumer consumer = list::add;
+ consumer.accept("test");
+ assertEquals(1, list.size());
+ assertEquals("test", list.get(0));
+ }
+
+ @Test
+ void test_whenAcceptIsInvokedWithNull_thenConsumesNull() throws Exception {
+ List list = new ArrayList<>();
+ ThrowingConsumer consumer = list::add;
+ consumer.accept(null);
+ assertEquals(1, list.size());
+ assertNull(list.get(0));
+ }
+
+ @Test
+ void test_whenAcceptThrowsException_thenExceptionIsPropagated() {
+ ThrowingConsumer consumer = (value) -> {
+ throw new IOException("Test exception");
+ };
+ Exception exception = assertThrows(Exception.class, () -> consumer.accept("test"));
+ assertEquals("Test exception", exception.getMessage());
+ assertInstanceOf(IOException.class, exception);
+ }
+
+ @Test
+ void test_whenAcceptIsInvokedMultipleTimes_thenConsumesAllValues() throws Exception {
+ List list = new ArrayList<>();
+ ThrowingConsumer consumer = list::add;
+ consumer.accept(1);
+ consumer.accept(2);
+ consumer.accept(3);
+ assertEquals(3, list.size());
+ assertEquals(1, list.get(0));
+ assertEquals(2, list.get(1));
+ assertEquals(3, list.get(2));
+ }
+
+ @Test
+ void test_whenAcceptIsInvokedWithComplexObject_thenConsumesObject() throws Exception {
+ List list = new ArrayList<>();
+ ThrowingConsumer consumer = list::add;
+ String[] array = { "a", "b", "c" };
+ consumer.accept(array);
+ assertEquals(1, list.size());
+ assertSame(array, list.get(0));
+ }
+}
diff --git a/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingFunctionTest.java b/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingFunctionTest.java
new file mode 100644
index 0000000..9065af2
--- /dev/null
+++ b/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingFunctionTest.java
@@ -0,0 +1,59 @@
+package org.zeplinko.commons.lang.ext.util.function;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ThrowingFunctionTest {
+
+ @Test
+ void test_whenApplyIsInvoked_thenTransformsValue() throws Exception {
+ ThrowingFunction function = String::length;
+ Integer result = function.apply("test");
+ assertEquals(4, result);
+ }
+
+ @Test
+ void test_whenApplyIsInvokedWithNull_thenHandlesNull() throws Exception {
+ ThrowingFunction function = (value) -> value == null ? "null" : value.toUpperCase();
+ String result = function.apply(null);
+ assertEquals("null", result);
+ }
+
+ @Test
+ void test_whenApplyThrowsException_thenExceptionIsPropagated() {
+ ThrowingFunction function = (value) -> {
+ throw new IOException("Test exception");
+ };
+ Exception exception = assertThrows(Exception.class, () -> function.apply("test"));
+ assertEquals("Test exception", exception.getMessage());
+ assertInstanceOf(IOException.class, exception);
+ }
+
+ @Test
+ void test_whenApplyIsInvokedMultipleTimes_thenTransformsEachValue() throws Exception {
+ ThrowingFunction function = (value) -> value * 2;
+ assertEquals(4, function.apply(2));
+ assertEquals(10, function.apply(5));
+ assertEquals(20, function.apply(10));
+ }
+
+ @Test
+ void test_whenApplyIsInvokedWithComplexTransformation_thenReturnsTransformedValue() throws Exception {
+ ThrowingFunction function = (value) -> value.split(",");
+ String[] result = function.apply("a,b,c");
+ assertEquals(3, result.length);
+ assertEquals("a", result[0]);
+ assertEquals("b", result[1]);
+ assertEquals("c", result[2]);
+ }
+
+ @Test
+ void test_whenApplyReturnsNull_thenReturnsNull() throws Exception {
+ ThrowingFunction function = (value) -> null;
+ String result = function.apply("test");
+ assertNull(result);
+ }
+}
diff --git a/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingRunnableTest.java b/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingRunnableTest.java
new file mode 100644
index 0000000..05433f8
--- /dev/null
+++ b/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingRunnableTest.java
@@ -0,0 +1,55 @@
+package org.zeplinko.commons.lang.ext.util.function;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ThrowingRunnableTest {
+
+ @Test
+ void test_whenRunIsInvoked_thenExecutesSuccessfully() throws Exception {
+ AtomicInteger counter = new AtomicInteger(0);
+ ThrowingRunnable runnable = counter::incrementAndGet;
+ runnable.run();
+ assertEquals(1, counter.get());
+ }
+
+ @Test
+ void test_whenRunIsInvokedMultipleTimes_thenExecutesEachTime() throws Exception {
+ AtomicInteger counter = new AtomicInteger(0);
+ ThrowingRunnable runnable = counter::incrementAndGet;
+ runnable.run();
+ runnable.run();
+ runnable.run();
+ assertEquals(3, counter.get());
+ }
+
+ @Test
+ void test_whenRunThrowsException_thenExceptionIsPropagated() {
+ ThrowingRunnable runnable = () -> {
+ throw new IOException("Test exception");
+ };
+ Exception exception = assertThrows(Exception.class, runnable::run);
+ assertEquals("Test exception", exception.getMessage());
+ assertInstanceOf(IOException.class, exception);
+ }
+
+ @Test
+ void test_whenRunIsInvokedWithSideEffect_thenSideEffectOccurs() throws Exception {
+ StringBuilder sb = new StringBuilder();
+ ThrowingRunnable runnable = () -> sb.append("executed");
+ runnable.run();
+ assertEquals("executed", sb.toString());
+ }
+
+ @Test
+ void test_whenRunIsInvokedWithNoOperation_thenNoExceptionIsThrown() {
+ ThrowingRunnable runnable = () -> {
+ // No operation
+ };
+ assertDoesNotThrow(runnable::run);
+ }
+}
diff --git a/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingSupplierTest.java b/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingSupplierTest.java
new file mode 100644
index 0000000..f82dd7d
--- /dev/null
+++ b/src/test/java/org/zeplinko/commons/lang/ext/util/function/ThrowingSupplierTest.java
@@ -0,0 +1,51 @@
+package org.zeplinko.commons.lang.ext.util.function;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ThrowingSupplierTest {
+
+ @Test
+ void test_whenGetIsInvoked_thenReturnsExpectedValue() throws Exception {
+ ThrowingSupplier supplier = () -> "test value";
+ String result = supplier.get();
+ assertEquals("test value", result);
+ }
+
+ @Test
+ void test_whenGetIsInvokedWithNull_thenReturnsNull() throws Exception {
+ ThrowingSupplier supplier = () -> null;
+ String result = supplier.get();
+ assertNull(result);
+ }
+
+ @Test
+ void test_whenGetThrowsException_thenExceptionIsPropagated() {
+ ThrowingSupplier supplier = () -> {
+ throw new IOException("Test exception");
+ };
+ Exception exception = assertThrows(Exception.class, supplier::get);
+ assertEquals("Test exception", exception.getMessage());
+ assertInstanceOf(IOException.class, exception);
+ }
+
+ @Test
+ void test_whenGetIsInvokedMultipleTimes_thenReturnsConsistentValue() throws Exception {
+ ThrowingSupplier supplier = () -> 42;
+ assertEquals(42, supplier.get());
+ assertEquals(42, supplier.get());
+ assertEquals(42, supplier.get());
+ }
+
+ @Test
+ void test_whenGetIsInvokedWithComplexObject_thenReturnsObject() throws Exception {
+ String[] expectedArray = { "a", "b", "c" };
+ ThrowingSupplier supplier = () -> expectedArray;
+ String[] result = supplier.get();
+ assertSame(expectedArray, result);
+ assertArrayEquals(expectedArray, result);
+ }
+}