diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 0000000..09d8305 --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,58 @@ +# Migrating from 1.x to 2.0 + +2.0 reconciles the long-divergent `1.6.x` and `main` lines into a single, +coherent API. It contains breaking changes — all small and mechanical. + +## Breaking changes + +### 1. Exception wrapper renamed and repackaged + +`com.pivovarit.function.exception.WrappedException` → +`com.pivovarit.function.CheckedException`. + +The `unchecked(...)` adapters still throw a `RuntimeException` subclass; only the +type name and package changed. Update imports and any `catch` clauses: + +```diff +- import com.pivovarit.function.exception.WrappedException; ++ import com.pivovarit.function.CheckedException; +... +- } catch (WrappedException e) { ++ } catch (CheckedException e) { +``` + +### 2. `ThrowingIntFunction` removed (no same-shape replacement) + +1.x shipped `ThrowingIntFunction` with SAM `R apply(int) throws E` — the +*primitive → object* (`IntFunction`) direction. It has been removed in 2.0. + +2.0 instead ships the *object → primitive* (`To*Function`) family: +`ThrowingToIntFunction` (`int applyAsInt(T)`), `ThrowingToLongFunction`, +and the new `ThrowingToDoubleFunction`. These are **not** drop-in replacements +for the old `ThrowingIntFunction` — the argument/result directions are reversed. + +If you relied on `ThrowingIntFunction` (`int` → object), use a standard +`Function` together with `unchecked()`/`sneaky()`, or box manually. + +### 3. `lifted()` / `lift()` renamed to `optional()` + +`ThrowingFunction.lifted(...)` (static) and `ThrowingFunction.lift()` (instance) +are both renamed to `optional()`, matching `ThrowingSupplier.optional(...)` and +`ThrowingBiFunction.optional(...)`. + +```diff +- ThrowingFunction.lifted(URI::new) ++ ThrowingFunction.optional(URI::new) +``` + +## New in 2.0 + +- **`recover(throwing, handler)`** on every interface — produce a fallback value + (or handle the side effect) instead of wrapping or rethrowing. For + single-argument interfaces the handler receives the input and the exception; + for two-argument interfaces it receives the exception. +- **`ThrowingToIntFunction`** and **`ThrowingToDoubleFunction`**, completing the + `To{Int,Long,Double}Function` primitive trio. +- **`ThrowingIntSupplier`**, **`ThrowingLongSupplier`**, **`ThrowingDoubleSupplier`**, + and **`ThrowingBooleanSupplier`** — primitive supplier variants, handy for I/O + reads of primitives (e.g. `DataInputStream::readInt`). diff --git a/README.md b/README.md index 364dd36..eb0f4a6 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![ci](https://github.com/pivovarit/throwing-function/actions/workflows/ci.yml/badge.svg)](https://github.com/pivovarit/throwing-function/actions/workflows/ci.yml) [![pitest](https://github.com/pivovarit/throwing-function/actions/workflows/pitest.yml/badge.svg?branch=main)](http://pivovarit.github.io/throwing-function/pitest) [![Maven Central Version](https://img.shields.io/maven-central/v/com.pivovarit/throwing-function)](https://central.sonatype.com/artifact/com.pivovarit/throwing-function/versions) -[![javadoc](https://javadoc.io/badge2/com.pivovarit/throwing-function/1.6.1/javadoc.svg)](https://javadoc.io/doc/com.pivovarit/throwing-function/1.6.1) +[![javadoc](https://javadoc.io/badge2/com.pivovarit/throwing-function/javadoc.svg)](https://javadoc.io/doc/com.pivovarit/throwing-function) [![libs.tech recommends](https://libs.tech/project/46967967/badge.svg)](https://libs.tech/project/46967967/throwing-function) [![Stargazers over time](https://starchart.cc/pivovarit/throwing-function.svg?variant=adaptive)](https://starchart.cc/pivovarit/throwing-function) @@ -57,12 +57,25 @@ stream.map(ThrowingFunction.unchecked(URI::new)) | `ThrowingPredicate` | `boolean test(T t) throws E` | | `ThrowingBiPredicate` | `boolean test(T t, U u) throws E` | | `ThrowingRunnable` | `void run() throws E` | +| `ThrowingToIntFunction` | `int applyAsInt(T t) throws E` | | `ThrowingToLongFunction` | `long applyAsLong(T t) throws E` | +| `ThrowingToDoubleFunction` | `double applyAsDouble(T t) throws E` | +| `ThrowingIntSupplier` | `int getAsInt() throws E` | +| `ThrowingLongSupplier` | `long getAsLong() throws E` | +| `ThrowingDoubleSupplier` | `double getAsDouble() throws E` | +| `ThrowingBooleanSupplier` | `boolean getAsBoolean() throws E` | ## Adapters Each interface provides static adapter methods to bridge `Throwing*` instances into standard `java.util.function` types. +| Adapter | On a checked exception it… | Returns | +|---|---|---| +| `unchecked` | wraps it in a `CheckedException` (a `RuntimeException`) and rethrows | the matching JDK type | +| `sneaky` | rethrows the original checked exception as-is | the matching JDK type | +| `optional` | swallows it and yields `Optional.empty()` | an `Optional`-returning type | +| `recover` | calls your handler to produce a fallback value | the matching JDK type | + ### `unchecked` — wrap in `CheckedException` Wraps the checked exception in a `CheckedException` (a `RuntimeException` subclass) and rethrows it. Available on all interfaces. @@ -81,19 +94,34 @@ stream.map(ThrowingFunction.sneaky(URI::new)) .forEach(System.out::println); ``` -### `lifted` / `optional` — return `Optional` +### `optional` — return `Optional` Returns the result wrapped in an `Optional`, or `Optional.empty()` on exception. No exception is propagated. -- `ThrowingFunction.lifted(f)` → `Function>` +- `ThrowingFunction.optional(f)` → `Function>` - `ThrowingBiFunction.optional(f)` → `BiFunction>` - `ThrowingSupplier.optional(s)` → `Supplier>` ```java -stream.map(ThrowingFunction.lifted(URI::new)) // Stream> +stream.map(ThrowingFunction.optional(URI::new)) // Stream> + .forEach(System.out::println); +``` + +### `recover` — fall back to a handler + +Runs the throwing lambda and, on a checked exception, invokes a handler that produces a fallback value (or handles the side effect) instead of wrapping or rethrowing. For single-argument interfaces the handler receives the input and the exception; for two-argument interfaces it receives the exception. + +```java +stream.map(ThrowingFunction.recover(URI::new, (path, e) -> URI.create("about:blank"))) .forEach(System.out::println); ``` +It also expresses the "default to a value on failure" case for predicates: + +```java +stream.filter(ThrowingPredicate.recover(Files::isHidden, (path, e) -> false)); +``` + ## Installation ### Maven @@ -102,16 +130,49 @@ stream.map(ThrowingFunction.lifted(URI::new)) // Stream> com.pivovarit throwing-function - 1.6.1 + 2.0.0 ``` ### Gradle ```groovy -implementation 'com.pivovarit:throwing-function:1.6.1' +implementation 'com.pivovarit:throwing-function:2.0.0' ``` ## Dependencies None. Implemented using core Java libraries only. + +## Requirements + +- Java 8 or newer +- Java module name `com.pivovarit.function` (an explicit `module-info` ships in a multi-release jar) +- Zero runtime dependencies + +## Cookbook + +Map into a primitive stream: + +```java +int totalBytes = paths.stream() + .mapToInt(ThrowingToIntFunction.unchecked(p -> Files.readAllBytes(p).length)) + .sum(); +``` + +Wrap a checked call for `CompletableFuture`: + +```java +CompletableFuture contents = CompletableFuture.supplyAsync( + ThrowingSupplier.unchecked(() -> Files.readAllBytes(path))); +``` + +Generate a primitive stream from a throwing source: + +```java +IntStream ints = IntStream.generate(ThrowingIntSupplier.unchecked(dataInput::readInt)); +``` + +## Migrating from 1.x + +2.0 contains small, mechanical breaking changes — the exception wrapper was renamed, `lifted`/`lift` became `optional`, and the primitive interfaces changed. See [MIGRATION.md](MIGRATION.md). diff --git a/src/main/java/com/pivovarit/function/ThrowingBiConsumer.java b/src/main/java/com/pivovarit/function/ThrowingBiConsumer.java index 36a4f5a..25a887d 100644 --- a/src/main/java/com/pivovarit/function/ThrowingBiConsumer.java +++ b/src/main/java/com/pivovarit/function/ThrowingBiConsumer.java @@ -16,6 +16,7 @@ package com.pivovarit.function; import java.util.function.BiConsumer; +import java.util.function.Consumer; import static java.util.Objects.requireNonNull; @@ -27,22 +28,22 @@ * * @param the type of the first argument to the operation * @param the type of the second argument to the operation - * @param the type of the thrown checked exception + * @param the type of the thrown checked exception * * @author Grzegorz Piwowarek * @see ThrowingConsumer */ @FunctionalInterface -public interface ThrowingBiConsumer { +public interface ThrowingBiConsumer { /** * Performs this operation on the given arguments. * - * @param t the first input argument + * @param t1 the first input argument * @param t2 the second input argument - * @throws EX the checked exception type + * @throws E the checked exception type */ - void accept(T1 t, T2 t2) throws EX; + void accept(T1 t1, T2 t2) throws E; /** * Returns a new BiConsumer instance which wraps the thrown checked exception instance into a {@link CheckedException} @@ -81,4 +82,27 @@ static BiConsumer sneaky(ThrowingBiConsumer the type of the first argument to the operation + * @param the type of the second argument to the operation + * @param consumer the ThrowingBiConsumer to wrap + * @param handler the recovery handler invoked with the thrown exception + * @return BiConsumer instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static BiConsumer recover(ThrowingBiConsumer consumer, Consumer handler) { + requireNonNull(consumer); + requireNonNull(handler); + return (arg1, arg2) -> { + try { + consumer.accept(arg1, arg2); + } catch (final Exception e) { + handler.accept(e); + } + }; + } } diff --git a/src/main/java/com/pivovarit/function/ThrowingBiFunction.java b/src/main/java/com/pivovarit/function/ThrowingBiFunction.java index 000b0d6..06c4301 100644 --- a/src/main/java/com/pivovarit/function/ThrowingBiFunction.java +++ b/src/main/java/com/pivovarit/function/ThrowingBiFunction.java @@ -17,6 +17,7 @@ import java.util.Optional; import java.util.function.BiFunction; +import java.util.function.Function; import static java.util.Objects.requireNonNull; @@ -106,4 +107,28 @@ static BiFunction> optional(ThrowingBiFunction 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 + * @param function the ThrowingBiFunction to wrap + * @param handler the recovery handler invoked with the thrown exception + * @return BiFunction instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static BiFunction recover(ThrowingBiFunction function, Function handler) { + requireNonNull(function); + requireNonNull(handler); + return (arg1, arg2) -> { + try { + return function.apply(arg1, arg2); + } catch (final Exception e) { + return handler.apply(e); + } + }; + } } diff --git a/src/main/java/com/pivovarit/function/ThrowingBiPredicate.java b/src/main/java/com/pivovarit/function/ThrowingBiPredicate.java index 2c7d292..3506909 100644 --- a/src/main/java/com/pivovarit/function/ThrowingBiPredicate.java +++ b/src/main/java/com/pivovarit/function/ThrowingBiPredicate.java @@ -17,6 +17,7 @@ import java.util.Objects; import java.util.function.BiPredicate; +import java.util.function.Predicate; import static java.util.Objects.requireNonNull; @@ -80,4 +81,27 @@ static BiPredicate sneaky(ThrowingBiPredicate the type of the first argument to the predicate + * @param the type of the second argument to the predicate + * @param predicate the ThrowingBiPredicate to wrap + * @param handler the recovery handler invoked with the thrown exception + * @return BiPredicate instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static BiPredicate recover(ThrowingBiPredicate predicate, Predicate handler) { + requireNonNull(predicate); + requireNonNull(handler); + return (arg1, arg2) -> { + try { + return predicate.test(arg1, arg2); + } catch (final Exception e) { + return handler.test(e); + } + }; + } } diff --git a/src/main/java/com/pivovarit/function/ThrowingBinaryOperator.java b/src/main/java/com/pivovarit/function/ThrowingBinaryOperator.java index 251d453..a547c36 100644 --- a/src/main/java/com/pivovarit/function/ThrowingBinaryOperator.java +++ b/src/main/java/com/pivovarit/function/ThrowingBinaryOperator.java @@ -16,6 +16,7 @@ package com.pivovarit.function; import java.util.function.BinaryOperator; +import java.util.function.Function; import static java.util.Objects.requireNonNull; @@ -72,4 +73,26 @@ static BinaryOperator sneaky(ThrowingBinaryOperator function) { } }; } + + /** + * Returns a new BinaryOperator instance which, in case of a thrown checked exception, returns the result + * produced by the supplied handler applied to the thrown exception + * + * @param the type of the operands and result of the operator + * @param operator the ThrowingBinaryOperator to wrap + * @param handler the recovery handler invoked with the thrown exception + * @return BinaryOperator instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static BinaryOperator recover(ThrowingBinaryOperator operator, Function handler) { + requireNonNull(operator); + requireNonNull(handler); + return (t1, t2) -> { + try { + return operator.apply(t1, t2); + } catch (final Exception e) { + return handler.apply(e); + } + }; + } } diff --git a/src/main/java/com/pivovarit/function/ThrowingBooleanSupplier.java b/src/main/java/com/pivovarit/function/ThrowingBooleanSupplier.java new file mode 100644 index 0000000..6d8993e --- /dev/null +++ b/src/main/java/com/pivovarit/function/ThrowingBooleanSupplier.java @@ -0,0 +1,95 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.util.function.BooleanSupplier; +import java.util.function.Predicate; + +import static java.util.Objects.requireNonNull; + +/** + * Represents a supplier of boolean-valued results; the supplier might throw a checked exception instance. + * + * @param the type of the thrown checked exception + * @author Grzegorz Piwowarek + * @since 2.0.0 + */ +@FunctionalInterface +public interface ThrowingBooleanSupplier { + + /** + * Gets a result. + * + * @return a result + * @throws E the checked exception type + */ + boolean getAsBoolean() throws E; + + /** + * Returns a new BooleanSupplier instance which wraps the thrown checked exception instance into a {@link CheckedException} + * + * @param supplier the ThrowingBooleanSupplier to wrap + * @return BooleanSupplier instance that wraps the checked exception into a {@link CheckedException} + */ + static BooleanSupplier unchecked(ThrowingBooleanSupplier supplier) { + requireNonNull(supplier); + return () -> { + try { + return supplier.getAsBoolean(); + } catch (final Exception e) { + throw new CheckedException(e); + } + }; + } + + /** + * Returns a new BooleanSupplier instance which rethrows the checked exception using the Sneaky Throws pattern + * + * @param supplier the ThrowingBooleanSupplier to wrap + * @return BooleanSupplier instance that rethrows the checked exception using the Sneaky Throws pattern + */ + static BooleanSupplier sneaky(ThrowingBooleanSupplier supplier) { + requireNonNull(supplier); + return () -> { + try { + return supplier.getAsBoolean(); + } catch (final Exception e) { + return SneakyThrowUtil.sneakyThrow(e); + } + }; + } + + /** + * Returns a new BooleanSupplier instance which, in case of a thrown checked exception, returns the result + * produced by the supplied handler applied to the thrown exception + * + * @param supplier the ThrowingBooleanSupplier to wrap + * @param handler the recovery handler invoked with the thrown exception + * @return BooleanSupplier instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static BooleanSupplier recover(ThrowingBooleanSupplier supplier, Predicate handler) { + requireNonNull(supplier); + requireNonNull(handler); + return () -> { + try { + return supplier.getAsBoolean(); + } catch (final Exception e) { + return handler.test(e); + } + }; + } +} diff --git a/src/main/java/com/pivovarit/function/ThrowingConsumer.java b/src/main/java/com/pivovarit/function/ThrowingConsumer.java index 710b35c..33b8bc9 100644 --- a/src/main/java/com/pivovarit/function/ThrowingConsumer.java +++ b/src/main/java/com/pivovarit/function/ThrowingConsumer.java @@ -16,6 +16,7 @@ package com.pivovarit.function; import java.util.Objects; +import java.util.function.BiConsumer; import java.util.function.Consumer; import static java.util.Objects.requireNonNull; @@ -75,4 +76,26 @@ static Consumer sneaky(ThrowingConsumer consumer) { } }; } + + /** + * Returns a new Consumer instance which, in case of a thrown checked exception, passes the input and the + * thrown exception to the supplied handler + * + * @param the type of the input to the operation + * @param consumer the ThrowingConsumer to wrap + * @param handler the recovery handler invoked with the input and the thrown exception + * @return Consumer instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static Consumer recover(ThrowingConsumer consumer, BiConsumer handler) { + requireNonNull(consumer); + requireNonNull(handler); + return t -> { + try { + consumer.accept(t); + } catch (final Exception e) { + handler.accept(t, e); + } + }; + } } diff --git a/src/main/java/com/pivovarit/function/ThrowingDoubleSupplier.java b/src/main/java/com/pivovarit/function/ThrowingDoubleSupplier.java new file mode 100644 index 0000000..22c6843 --- /dev/null +++ b/src/main/java/com/pivovarit/function/ThrowingDoubleSupplier.java @@ -0,0 +1,95 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.util.function.DoubleSupplier; +import java.util.function.ToDoubleFunction; + +import static java.util.Objects.requireNonNull; + +/** + * Represents a supplier of double-valued results; the supplier might throw a checked exception instance. + * + * @param the type of the thrown checked exception + * @author Grzegorz Piwowarek + * @since 2.0.0 + */ +@FunctionalInterface +public interface ThrowingDoubleSupplier { + + /** + * Gets a result. + * + * @return a result + * @throws E the checked exception type + */ + double getAsDouble() throws E; + + /** + * Returns a new DoubleSupplier instance which wraps the thrown checked exception instance into a {@link CheckedException} + * + * @param supplier the ThrowingDoubleSupplier to wrap + * @return DoubleSupplier instance that wraps the checked exception into a {@link CheckedException} + */ + static DoubleSupplier unchecked(ThrowingDoubleSupplier supplier) { + requireNonNull(supplier); + return () -> { + try { + return supplier.getAsDouble(); + } catch (final Exception e) { + throw new CheckedException(e); + } + }; + } + + /** + * Returns a new DoubleSupplier instance which rethrows the checked exception using the Sneaky Throws pattern + * + * @param supplier the ThrowingDoubleSupplier to wrap + * @return DoubleSupplier instance that rethrows the checked exception using the Sneaky Throws pattern + */ + static DoubleSupplier sneaky(ThrowingDoubleSupplier supplier) { + requireNonNull(supplier); + return () -> { + try { + return supplier.getAsDouble(); + } catch (final Exception e) { + return SneakyThrowUtil.sneakyThrow(e); + } + }; + } + + /** + * Returns a new DoubleSupplier instance which, in case of a thrown checked exception, returns the result + * produced by the supplied handler applied to the thrown exception + * + * @param supplier the ThrowingDoubleSupplier to wrap + * @param handler the recovery handler invoked with the thrown exception + * @return DoubleSupplier instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static DoubleSupplier recover(ThrowingDoubleSupplier supplier, ToDoubleFunction handler) { + requireNonNull(supplier); + requireNonNull(handler); + return () -> { + try { + return supplier.getAsDouble(); + } catch (final Exception e) { + return handler.applyAsDouble(e); + } + }; + } +} diff --git a/src/main/java/com/pivovarit/function/ThrowingFunction.java b/src/main/java/com/pivovarit/function/ThrowingFunction.java index c0245ad..07668c6 100644 --- a/src/main/java/com/pivovarit/function/ThrowingFunction.java +++ b/src/main/java/com/pivovarit/function/ThrowingFunction.java @@ -16,6 +16,7 @@ package com.pivovarit.function; import java.util.Optional; +import java.util.function.BiFunction; import java.util.function.Function; import static java.util.Objects.requireNonNull; @@ -48,8 +49,9 @@ public interface ThrowingFunction { * @param function the ThrowingFunction to wrap * @return a Function that returns the result of the given function as an Optional instance. * In case of a failure, empty Optional is returned + * @since 2.0.0 */ - static Function> lifted(final ThrowingFunction function) { + static Function> optional(final ThrowingFunction function) { requireNonNull(function); return t -> { @@ -83,12 +85,12 @@ static Function unchecked(final ThrowingFunction the type of the input to the function + * @param the type of the input to the function * @param the type of the result of the function * @param function the ThrowingFunction to wrap * @return Function instance that rethrows the checked exception using the Sneaky Throws pattern */ - static Function sneaky(ThrowingFunction function) { + static Function sneaky(ThrowingFunction function) { requireNonNull(function); return t -> { try { @@ -103,8 +105,9 @@ static Function sneaky(ThrowingFunction> lift() { + default Function> optional() { return t -> { try { return Optional.ofNullable(apply(t)); @@ -113,4 +116,27 @@ default Function> lift() { } }; } + + /** + * Returns a new Function instance which, in case of a thrown checked exception, returns the result + * produced by the supplied handler applied to the input and the thrown exception + * + * @param the type of the input to the function + * @param the type of the result of the function + * @param function the ThrowingFunction to wrap + * @param handler the recovery handler invoked with the input and the thrown exception + * @return Function instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static Function recover(ThrowingFunction function, BiFunction handler) { + requireNonNull(function); + requireNonNull(handler); + return t -> { + try { + return function.apply(t); + } catch (final Exception e) { + return handler.apply(t, e); + } + }; + } } diff --git a/src/main/java/com/pivovarit/function/ThrowingIntSupplier.java b/src/main/java/com/pivovarit/function/ThrowingIntSupplier.java new file mode 100644 index 0000000..4214fe0 --- /dev/null +++ b/src/main/java/com/pivovarit/function/ThrowingIntSupplier.java @@ -0,0 +1,95 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.util.function.IntSupplier; +import java.util.function.ToIntFunction; + +import static java.util.Objects.requireNonNull; + +/** + * Represents a supplier of int-valued results; the supplier might throw a checked exception instance. + * + * @param the type of the thrown checked exception + * @author Grzegorz Piwowarek + * @since 2.0.0 + */ +@FunctionalInterface +public interface ThrowingIntSupplier { + + /** + * Gets a result. + * + * @return a result + * @throws E the checked exception type + */ + int getAsInt() throws E; + + /** + * Returns a new IntSupplier instance which wraps the thrown checked exception instance into a {@link CheckedException} + * + * @param supplier the ThrowingIntSupplier to wrap + * @return IntSupplier instance that wraps the checked exception into a {@link CheckedException} + */ + static IntSupplier unchecked(ThrowingIntSupplier supplier) { + requireNonNull(supplier); + return () -> { + try { + return supplier.getAsInt(); + } catch (final Exception e) { + throw new CheckedException(e); + } + }; + } + + /** + * Returns a new IntSupplier instance which rethrows the checked exception using the Sneaky Throws pattern + * + * @param supplier the ThrowingIntSupplier to wrap + * @return IntSupplier instance that rethrows the checked exception using the Sneaky Throws pattern + */ + static IntSupplier sneaky(ThrowingIntSupplier supplier) { + requireNonNull(supplier); + return () -> { + try { + return supplier.getAsInt(); + } catch (final Exception e) { + return SneakyThrowUtil.sneakyThrow(e); + } + }; + } + + /** + * Returns a new IntSupplier instance which, in case of a thrown checked exception, returns the result + * produced by the supplied handler applied to the thrown exception + * + * @param supplier the ThrowingIntSupplier to wrap + * @param handler the recovery handler invoked with the thrown exception + * @return IntSupplier instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static IntSupplier recover(ThrowingIntSupplier supplier, ToIntFunction handler) { + requireNonNull(supplier); + requireNonNull(handler); + return () -> { + try { + return supplier.getAsInt(); + } catch (final Exception e) { + return handler.applyAsInt(e); + } + }; + } +} diff --git a/src/main/java/com/pivovarit/function/ThrowingLongSupplier.java b/src/main/java/com/pivovarit/function/ThrowingLongSupplier.java new file mode 100644 index 0000000..c89ba04 --- /dev/null +++ b/src/main/java/com/pivovarit/function/ThrowingLongSupplier.java @@ -0,0 +1,95 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.util.function.LongSupplier; +import java.util.function.ToLongFunction; + +import static java.util.Objects.requireNonNull; + +/** + * Represents a supplier of long-valued results; the supplier might throw a checked exception instance. + * + * @param the type of the thrown checked exception + * @author Grzegorz Piwowarek + * @since 2.0.0 + */ +@FunctionalInterface +public interface ThrowingLongSupplier { + + /** + * Gets a result. + * + * @return a result + * @throws E the checked exception type + */ + long getAsLong() throws E; + + /** + * Returns a new LongSupplier instance which wraps the thrown checked exception instance into a {@link CheckedException} + * + * @param supplier the ThrowingLongSupplier to wrap + * @return LongSupplier instance that wraps the checked exception into a {@link CheckedException} + */ + static LongSupplier unchecked(ThrowingLongSupplier supplier) { + requireNonNull(supplier); + return () -> { + try { + return supplier.getAsLong(); + } catch (final Exception e) { + throw new CheckedException(e); + } + }; + } + + /** + * Returns a new LongSupplier instance which rethrows the checked exception using the Sneaky Throws pattern + * + * @param supplier the ThrowingLongSupplier to wrap + * @return LongSupplier instance that rethrows the checked exception using the Sneaky Throws pattern + */ + static LongSupplier sneaky(ThrowingLongSupplier supplier) { + requireNonNull(supplier); + return () -> { + try { + return supplier.getAsLong(); + } catch (final Exception e) { + return SneakyThrowUtil.sneakyThrow(e); + } + }; + } + + /** + * Returns a new LongSupplier instance which, in case of a thrown checked exception, returns the result + * produced by the supplied handler applied to the thrown exception + * + * @param supplier the ThrowingLongSupplier to wrap + * @param handler the recovery handler invoked with the thrown exception + * @return LongSupplier instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static LongSupplier recover(ThrowingLongSupplier supplier, ToLongFunction handler) { + requireNonNull(supplier); + requireNonNull(handler); + return () -> { + try { + return supplier.getAsLong(); + } catch (final Exception e) { + return handler.applyAsLong(e); + } + }; + } +} diff --git a/src/main/java/com/pivovarit/function/ThrowingPredicate.java b/src/main/java/com/pivovarit/function/ThrowingPredicate.java index 2110c2d..aea5c08 100644 --- a/src/main/java/com/pivovarit/function/ThrowingPredicate.java +++ b/src/main/java/com/pivovarit/function/ThrowingPredicate.java @@ -16,6 +16,7 @@ package com.pivovarit.function; import java.util.Objects; +import java.util.function.BiPredicate; import java.util.function.Predicate; import static java.util.Objects.requireNonNull; @@ -75,4 +76,26 @@ static Predicate sneaky(ThrowingPredicate predicate) { } }; } + + /** + * Returns a new Predicate instance which, in case of a thrown checked exception, returns the result + * produced by the supplied handler applied to the input and the thrown exception + * + * @param the type of the input to the predicate + * @param predicate the ThrowingPredicate to wrap + * @param handler the recovery handler invoked with the input and the thrown exception + * @return Predicate instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static Predicate recover(ThrowingPredicate predicate, BiPredicate handler) { + requireNonNull(predicate); + requireNonNull(handler); + return t -> { + try { + return predicate.test(t); + } catch (final Exception e) { + return handler.test(t, e); + } + }; + } } diff --git a/src/main/java/com/pivovarit/function/ThrowingRunnable.java b/src/main/java/com/pivovarit/function/ThrowingRunnable.java index fd56a9b..7cd0b03 100644 --- a/src/main/java/com/pivovarit/function/ThrowingRunnable.java +++ b/src/main/java/com/pivovarit/function/ThrowingRunnable.java @@ -16,6 +16,7 @@ package com.pivovarit.function; import java.util.Objects; +import java.util.function.Consumer; import static java.util.Objects.requireNonNull; @@ -69,4 +70,25 @@ static Runnable sneaky(ThrowingRunnable runnable) { } }; } + + /** + * Returns a new Runnable instance which, in case of a thrown checked exception, passes the thrown exception + * to the supplied handler + * + * @param runnable the ThrowingRunnable to wrap + * @param handler the recovery handler invoked with the thrown exception + * @return Runnable instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static Runnable recover(ThrowingRunnable runnable, Consumer handler) { + requireNonNull(runnable); + requireNonNull(handler); + return () -> { + try { + runnable.run(); + } catch (final Exception e) { + handler.accept(e); + } + }; + } } diff --git a/src/main/java/com/pivovarit/function/ThrowingSupplier.java b/src/main/java/com/pivovarit/function/ThrowingSupplier.java index c21e46e..9a0f99a 100644 --- a/src/main/java/com/pivovarit/function/ThrowingSupplier.java +++ b/src/main/java/com/pivovarit/function/ThrowingSupplier.java @@ -16,6 +16,7 @@ package com.pivovarit.function; import java.util.Optional; +import java.util.function.Function; import java.util.function.Supplier; import static java.util.Objects.requireNonNull; @@ -91,4 +92,26 @@ static Supplier sneaky(ThrowingSupplier supplier) { } }; } + + /** + * Returns a new Supplier instance which, in case of a thrown checked exception, returns the result + * produced by the supplied handler applied to the thrown exception + * + * @param the type of results supplied by this supplier + * @param supplier the ThrowingSupplier to wrap + * @param handler the recovery handler invoked with the thrown exception + * @return Supplier instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static Supplier recover(ThrowingSupplier supplier, Function handler) { + requireNonNull(supplier); + requireNonNull(handler); + return () -> { + try { + return supplier.get(); + } catch (final Exception e) { + return handler.apply(e); + } + }; + } } diff --git a/src/main/java/com/pivovarit/function/ThrowingToDoubleFunction.java b/src/main/java/com/pivovarit/function/ThrowingToDoubleFunction.java new file mode 100644 index 0000000..ac01048 --- /dev/null +++ b/src/main/java/com/pivovarit/function/ThrowingToDoubleFunction.java @@ -0,0 +1,101 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.util.function.ToDoubleBiFunction; +import java.util.function.ToDoubleFunction; + +import static java.util.Objects.requireNonNull; + +/** + * Represents a function that accepts one argument and produces a double-valued result; + * the function might throw a checked exception instance. + * + * @param the type of the input to the function + * @param the type of the thrown checked exception + * @author Grzegorz Piwowarek + * @since 2.0.0 + */ +@FunctionalInterface +public interface ThrowingToDoubleFunction { + + /** + * Applies this function to the given argument. + * + * @param t the function argument + * @return the function result + * @throws E the checked exception type + */ + double applyAsDouble(T t) throws E; + + /** + * Returns a new ToDoubleFunction instance which wraps the thrown checked exception instance into a {@link CheckedException} + * + * @param the type of the input to the function + * @param function the ThrowingToDoubleFunction to wrap + * @return ToDoubleFunction instance that wraps the checked exception into a {@link CheckedException} + */ + static ToDoubleFunction unchecked(ThrowingToDoubleFunction function) { + requireNonNull(function); + return t -> { + try { + return function.applyAsDouble(t); + } catch (final Exception e) { + throw new CheckedException(e); + } + }; + } + + /** + * Returns a new ToDoubleFunction instance which rethrows the checked exception using the Sneaky Throws pattern + * + * @param the type of the input to the function + * @param function the ThrowingToDoubleFunction to wrap + * @return ToDoubleFunction instance that rethrows the checked exception using the Sneaky Throws pattern + */ + static ToDoubleFunction sneaky(ThrowingToDoubleFunction function) { + requireNonNull(function); + return t -> { + try { + return function.applyAsDouble(t); + } catch (final Exception e) { + return SneakyThrowUtil.sneakyThrow(e); + } + }; + } + + /** + * Returns a new ToDoubleFunction instance which, in case of a thrown checked exception, returns the result + * produced by the supplied handler applied to the input and the thrown exception + * + * @param the type of the input to the function + * @param function the ThrowingToDoubleFunction to wrap + * @param handler the recovery handler invoked with the input and the thrown exception + * @return ToDoubleFunction instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static ToDoubleFunction recover(ThrowingToDoubleFunction function, ToDoubleBiFunction handler) { + requireNonNull(function); + requireNonNull(handler); + return t -> { + try { + return function.applyAsDouble(t); + } catch (final Exception e) { + return handler.applyAsDouble(t, e); + } + }; + } +} diff --git a/src/main/java/com/pivovarit/function/ThrowingToIntFunction.java b/src/main/java/com/pivovarit/function/ThrowingToIntFunction.java new file mode 100644 index 0000000..98929ed --- /dev/null +++ b/src/main/java/com/pivovarit/function/ThrowingToIntFunction.java @@ -0,0 +1,101 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.util.function.ToIntBiFunction; +import java.util.function.ToIntFunction; + +import static java.util.Objects.requireNonNull; + +/** + * Represents a function that accepts one argument and produces an int-valued result; + * the function might throw a checked exception instance. + * + * @param the type of the input to the function + * @param the type of the thrown checked exception + * @author Grzegorz Piwowarek + * @since 2.0.0 + */ +@FunctionalInterface +public interface ThrowingToIntFunction { + + /** + * Applies this function to the given argument. + * + * @param t the function argument + * @return the function result + * @throws E the checked exception type + */ + int applyAsInt(T t) throws E; + + /** + * Returns a new ToIntFunction instance which wraps the thrown checked exception instance into a {@link CheckedException} + * + * @param the type of the input to the function + * @param function the ThrowingToIntFunction to wrap + * @return ToIntFunction instance that wraps the checked exception into a {@link CheckedException} + */ + static ToIntFunction unchecked(ThrowingToIntFunction function) { + requireNonNull(function); + return t -> { + try { + return function.applyAsInt(t); + } catch (final Exception e) { + throw new CheckedException(e); + } + }; + } + + /** + * Returns a new ToIntFunction instance which rethrows the checked exception using the Sneaky Throws pattern + * + * @param the type of the input to the function + * @param function the ThrowingToIntFunction to wrap + * @return ToIntFunction instance that rethrows the checked exception using the Sneaky Throws pattern + */ + static ToIntFunction sneaky(ThrowingToIntFunction function) { + requireNonNull(function); + return t -> { + try { + return function.applyAsInt(t); + } catch (final Exception e) { + return SneakyThrowUtil.sneakyThrow(e); + } + }; + } + + /** + * Returns a new ToIntFunction instance which, in case of a thrown checked exception, returns the result + * produced by the supplied handler applied to the input and the thrown exception + * + * @param the type of the input to the function + * @param function the ThrowingToIntFunction to wrap + * @param handler the recovery handler invoked with the input and the thrown exception + * @return ToIntFunction instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static ToIntFunction recover(ThrowingToIntFunction function, ToIntBiFunction handler) { + requireNonNull(function); + requireNonNull(handler); + return t -> { + try { + return function.applyAsInt(t); + } catch (final Exception e) { + return handler.applyAsInt(t, e); + } + }; + } +} diff --git a/src/main/java/com/pivovarit/function/ThrowingToLongFunction.java b/src/main/java/com/pivovarit/function/ThrowingToLongFunction.java index 424ee24..f579ee6 100644 --- a/src/main/java/com/pivovarit/function/ThrowingToLongFunction.java +++ b/src/main/java/com/pivovarit/function/ThrowingToLongFunction.java @@ -15,6 +15,7 @@ */ package com.pivovarit.function; +import java.util.function.ToLongBiFunction; import java.util.function.ToLongFunction; import static java.util.Objects.requireNonNull; @@ -59,11 +60,11 @@ static ToLongFunction unchecked(final ThrowingToLongFunction the type of the input to the function + * @param the type of the input to the function * @param function the ThrowingToLongFunction to wrap * @return ToLongFunction instance that rethrows the checked exception using the Sneaky Throws pattern */ - static ToLongFunction sneaky(ThrowingToLongFunction function) { + static ToLongFunction sneaky(ThrowingToLongFunction function) { requireNonNull(function); return t -> { try { @@ -73,4 +74,26 @@ static ToLongFunction sneaky(ThrowingToLongFunction func } }; } + + /** + * Returns a new ToLongFunction instance which, in case of a thrown checked exception, returns the result + * produced by the supplied handler applied to the input and the thrown exception + * + * @param the type of the input to the function + * @param function the ThrowingToLongFunction to wrap + * @param handler the recovery handler invoked with the input and the thrown exception + * @return ToLongFunction instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static ToLongFunction recover(ThrowingToLongFunction function, ToLongBiFunction handler) { + requireNonNull(function); + requireNonNull(handler); + return t -> { + try { + return function.applyAsLong(t); + } catch (final Exception e) { + return handler.applyAsLong(t, e); + } + }; + } } diff --git a/src/main/java/com/pivovarit/function/ThrowingUnaryOperator.java b/src/main/java/com/pivovarit/function/ThrowingUnaryOperator.java index ccac6f0..b7fc998 100644 --- a/src/main/java/com/pivovarit/function/ThrowingUnaryOperator.java +++ b/src/main/java/com/pivovarit/function/ThrowingUnaryOperator.java @@ -15,6 +15,7 @@ */ package com.pivovarit.function; +import java.util.function.BiFunction; import java.util.function.UnaryOperator; import static java.util.Objects.requireNonNull; @@ -70,4 +71,26 @@ static UnaryOperator sneaky(ThrowingUnaryOperator operator) { } }; } + + /** + * Returns a new UnaryOperator instance which, in case of a thrown checked exception, returns the result + * produced by the supplied handler applied to the input and the thrown exception + * + * @param the type of the operand and result of the operator + * @param operator the ThrowingUnaryOperator to wrap + * @param handler the recovery handler invoked with the input and the thrown exception + * @return UnaryOperator instance that recovers from a thrown checked exception using the supplied handler + * @since 2.0.0 + */ + static UnaryOperator recover(ThrowingUnaryOperator operator, BiFunction handler) { + requireNonNull(operator); + requireNonNull(handler); + return t -> { + try { + return operator.apply(t); + } catch (final Exception e) { + return handler.apply(t, e); + } + }; + } } diff --git a/src/test/java/com/pivovarit/function/CheckedExceptionTest.java b/src/test/java/com/pivovarit/function/CheckedExceptionTest.java new file mode 100644 index 0000000..d3dc7ef --- /dev/null +++ b/src/test/java/com/pivovarit/function/CheckedExceptionTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.io.IOException; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class CheckedExceptionTest { + + @Test + void shouldPreserveCause() { + IOException cause = new IOException("disk full"); + CheckedException ex = new CheckedException(cause); + assertThat(ex.getCause()).isSameAs(cause); + } + + @Test + void shouldPreserveCauseMessage() { + IOException cause = new IOException("disk full"); + CheckedException ex = new CheckedException(cause); + assertThat(ex.getMessage()).isEqualTo("disk full"); + } + + @Test + void shouldBeARuntimeException() { + assertThat(new CheckedException(new IOException())).isInstanceOf(RuntimeException.class); + } +} diff --git a/src/test/java/com/pivovarit/function/RecoverTest.java b/src/test/java/com/pivovarit/function/RecoverTest.java new file mode 100644 index 0000000..0551dcd --- /dev/null +++ b/src/test/java/com/pivovarit/function/RecoverTest.java @@ -0,0 +1,253 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.BinaryOperator; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.ToDoubleFunction; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; +import java.util.function.UnaryOperator; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class RecoverTest { + + @Test + void functionRecoversUsingInputAndException() { + ThrowingFunction f = s -> { throw new Exception("E"); }; + Function recovered = ThrowingFunction.recover(f, (s, e) -> s + ":" + e.getMessage()); + assertThat(recovered.apply("in")).isEqualTo("in:E"); + } + + @Test + void functionDoesNotInvokeHandlerOnSuccess() { + AtomicReference handlerCalled = new AtomicReference<>(); + Function recovered = ThrowingFunction.recover( + Integer::parseInt, (s, e) -> { handlerCalled.set(s); return -1; }); + assertThat(recovered.apply("42")).isEqualTo(42); + assertThat(handlerCalled.get()).isNull(); + } + + @Test + void unaryOperatorRecoversUsingInputAndException() { + ThrowingUnaryOperator op = s -> { throw new Exception("boom"); }; + UnaryOperator recovered = ThrowingUnaryOperator.recover(op, (s, e) -> s.toUpperCase()); + assertThat(recovered.apply("x")).isEqualTo("X"); + } + + @Test + void toLongFunctionRecoversUsingInputAndException() { + ThrowingToLongFunction f = s -> { throw new Exception(); }; + ToLongFunction recovered = ThrowingToLongFunction.recover(f, (s, e) -> (long) s.length()); + assertThat(recovered.applyAsLong("abcd")).isEqualTo(4L); + } + + @Test + void predicateRecoversToFalse() { + ThrowingPredicate p = s -> { throw new Exception(); }; + Predicate recovered = ThrowingPredicate.recover(p, (s, e) -> false); + assertThat(recovered.test("x")).isFalse(); + } + + @Test + void consumerRecoversUsingInputAndException() { + AtomicReference captured = new AtomicReference<>(); + ThrowingConsumer c = s -> { throw new Exception(s); }; + Consumer recovered = ThrowingConsumer.recover(c, (s, e) -> captured.set(s + ":" + e.getMessage())); + recovered.accept("x"); + assertThat(captured.get()).isEqualTo("x:x"); + } + + @Test + void runnableRecoversUsingException() { + AtomicReference captured = new AtomicReference<>(); + ThrowingRunnable r = () -> { throw new Exception("failed"); }; + Runnable recovered = ThrowingRunnable.recover(r, e -> captured.set(e.getMessage())); + recovered.run(); + assertThat(captured.get()).isEqualTo("failed"); + } + + @Test + void supplierRecoversUsingException() { + ThrowingSupplier s = () -> { throw new Exception("oops"); }; + Supplier recovered = ThrowingSupplier.recover(s, e -> "fallback:" + e.getMessage()); + assertThat(recovered.get()).isEqualTo("fallback:oops"); + } + + @Test + void biFunctionRecoversUsingException() { + ThrowingBiFunction f = (a, b) -> { throw new Exception("x"); }; + BiFunction recovered = ThrowingBiFunction.recover(f, e -> "fallback"); + assertThat(recovered.apply("a", "b")).isEqualTo("fallback"); + } + + @Test + void binaryOperatorRecoversUsingException() { + ThrowingBinaryOperator op = (a, b) -> { throw new Exception(); }; + BinaryOperator recovered = ThrowingBinaryOperator.recover(op, e -> "default"); + assertThat(recovered.apply("a", "b")).isEqualTo("default"); + } + + @Test + void biConsumerRecoversUsingException() { + AtomicReference captured = new AtomicReference<>(); + ThrowingBiConsumer c = (a, b) -> { throw new Exception("e"); }; + BiConsumer recovered = ThrowingBiConsumer.recover(c, e -> captured.set(e.getMessage())); + recovered.accept("a", "b"); + assertThat(captured.get()).isEqualTo("e"); + } + + @Test + void biPredicateRecoversToFalse() { + ThrowingBiPredicate p = (a, b) -> { throw new Exception(); }; + BiPredicate recovered = ThrowingBiPredicate.recover(p, e -> false); + assertThat(recovered.test("a", "b")).isFalse(); + } + + @Test + void unaryOperatorPassesThroughOnSuccess() { + UnaryOperator recovered = ThrowingUnaryOperator.recover(s -> s + "!", (s, e) -> "X"); + assertThat(recovered.apply("hi")).isEqualTo("hi!"); + } + + @Test + void toIntFunctionPassesThroughOnSuccess() { + ToIntFunction recovered = ThrowingToIntFunction.recover(s -> 7, (s, e) -> -1); + assertThat(recovered.applyAsInt("x")).isEqualTo(7); + } + + @Test + void toLongFunctionPassesThroughOnSuccess() { + ToLongFunction recovered = ThrowingToLongFunction.recover(s -> 7L, (s, e) -> -1L); + assertThat(recovered.applyAsLong("x")).isEqualTo(7L); + } + + @Test + void toDoubleFunctionPassesThroughOnSuccess() { + ToDoubleFunction recovered = ThrowingToDoubleFunction.recover(s -> 7.0, (s, e) -> -1.0); + assertThat(recovered.applyAsDouble("x")).isEqualTo(7.0); + } + + @Test + void predicatePassesThroughOnSuccess() { + Predicate recovered = ThrowingPredicate.recover(s -> true, (s, e) -> false); + assertThat(recovered.test("x")).isTrue(); + } + + @Test + void predicateRecoversToTrue() { + ThrowingPredicate p = s -> { throw new Exception(); }; + Predicate recovered = ThrowingPredicate.recover(p, (s, e) -> true); + assertThat(recovered.test("x")).isTrue(); + } + + @Test + void supplierPassesThroughOnSuccess() { + Supplier recovered = ThrowingSupplier.recover(() -> "ok", e -> "fallback"); + assertThat(recovered.get()).isEqualTo("ok"); + } + + @Test + void consumerDoesNotInvokeHandlerOnSuccess() { + AtomicReference sideEffect = new AtomicReference<>(); + AtomicReference handled = new AtomicReference<>(); + Consumer recovered = ThrowingConsumer.recover(sideEffect::set, (s, e) -> handled.set(s)); + recovered.accept("hello"); + assertThat(sideEffect.get()).isEqualTo("hello"); + assertThat(handled.get()).isNull(); + } + + @Test + void runnableDoesNotInvokeHandlerOnSuccess() { + AtomicReference handled = new AtomicReference<>(); + Runnable recovered = ThrowingRunnable.recover(() -> {}, e -> handled.set("x")); + recovered.run(); + assertThat(handled.get()).isNull(); + } + + @Test + void biFunctionPassesThroughOnSuccess() { + BiFunction recovered = ThrowingBiFunction.recover((a, b) -> a + b, e -> "fallback"); + assertThat(recovered.apply("a", "b")).isEqualTo("ab"); + } + + @Test + void binaryOperatorPassesThroughOnSuccess() { + BinaryOperator recovered = ThrowingBinaryOperator.recover((a, b) -> a + b, e -> "default"); + assertThat(recovered.apply("a", "b")).isEqualTo("ab"); + } + + @Test + void biConsumerDoesNotInvokeHandlerOnSuccess() { + AtomicReference sideEffect = new AtomicReference<>(); + AtomicReference handled = new AtomicReference<>(); + BiConsumer recovered = ThrowingBiConsumer.recover((a, b) -> sideEffect.set(a + b), e -> handled.set("x")); + recovered.accept("a", "b"); + assertThat(sideEffect.get()).isEqualTo("ab"); + assertThat(handled.get()).isNull(); + } + + @Test + void biPredicatePassesThroughOnSuccess() { + BiPredicate recovered = ThrowingBiPredicate.recover((a, b) -> true, e -> false); + assertThat(recovered.test("a", "b")).isTrue(); + } + + @Test + void predicatePassesFalseThroughOnSuccess() { + Predicate recovered = ThrowingPredicate.recover(s -> false, (s, e) -> true); + assertThat(recovered.test("x")).isFalse(); + } + + @Test + void biPredicatePassesFalseThroughOnSuccess() { + BiPredicate recovered = ThrowingBiPredicate.recover((a, b) -> false, e -> true); + assertThat(recovered.test("a", "b")).isFalse(); + } + + @Test + void biPredicateRecoversToTrue() { + ThrowingBiPredicate p = (a, b) -> { throw new Exception(); }; + BiPredicate recovered = ThrowingBiPredicate.recover(p, e -> true); + assertThat(recovered.test("a", "b")).isTrue(); + } + + @Test + void rejectsNullArguments() { + ThrowingFunction f = s -> s; + assertThatThrownBy(() -> ThrowingFunction.recover(f, null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> ThrowingFunction.recover(null, (s, e) -> s)).isInstanceOf(NullPointerException.class); + + ThrowingSupplier s = () -> "x"; + assertThatThrownBy(() -> ThrowingSupplier.recover(s, null)).isInstanceOf(NullPointerException.class); + + ThrowingBiFunction bf = (a, b) -> a; + assertThatThrownBy(() -> ThrowingBiFunction.recover(bf, null)).isInstanceOf(NullPointerException.class); + + ThrowingRunnable r = () -> {}; + assertThatThrownBy(() -> ThrowingRunnable.recover(r, null)).isInstanceOf(NullPointerException.class); + } +} diff --git a/src/test/java/com/pivovarit/function/SneakyThrowUtilTest.java b/src/test/java/com/pivovarit/function/SneakyThrowUtilTest.java new file mode 100644 index 0000000..f2ce7e4 --- /dev/null +++ b/src/test/java/com/pivovarit/function/SneakyThrowUtilTest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.io.IOException; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class SneakyThrowUtilTest { + + @Test + void shouldRethrowOriginalCheckedExceptionUnwrapped() { + IOException original = new IOException("io"); + assertThatThrownBy(() -> SneakyThrowUtil.sneakyThrow(original)) + .isSameAs(original) + .hasNoCause(); + } + + @Test + void shouldRethrowAsDeclaredCheckedTypeWithoutCompilerEnforcement() { + IOException original = new IOException("io"); + assertThatThrownBy(() -> SneakyThrowUtil.sneakyThrow(original)) + .isInstanceOf(IOException.class) + .hasMessage("io"); + } +} diff --git a/src/test/java/com/pivovarit/function/ThrowingBiConsumerTest.java b/src/test/java/com/pivovarit/function/ThrowingBiConsumerTest.java index c396e7f..0d0a991 100644 --- a/src/test/java/com/pivovarit/function/ThrowingBiConsumerTest.java +++ b/src/test/java/com/pivovarit/function/ThrowingBiConsumerTest.java @@ -26,15 +26,12 @@ class ThrowingBiConsumerTest { @Test void shouldConsume() throws Exception { - // given LongAdder input = new LongAdder(); ThrowingBiConsumer consumer = (i, j) -> input.increment(); - // when consumer.accept(2, 3); - // then assertThat(input.sum()).isEqualTo(1); } @@ -42,10 +39,8 @@ void shouldConsume() throws Exception { void shouldConsumeAndThrowUnchecked() { IOException cause = new IOException("some message"); - // given ThrowingBiConsumer consumer = (i, j) -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingBiConsumer.unchecked(consumer).accept(3, 3)) .hasMessage(cause.getMessage()) .isInstanceOf(CheckedException.class) @@ -54,23 +49,17 @@ void shouldConsumeAndThrowUnchecked() { @Test void shouldConsumeUnchecked() { - // given ThrowingBiConsumer consumer = (i, j) -> {}; - // when ThrowingBiConsumer.unchecked(consumer).accept(3, 4); - - // then no exception thrown } @Test void shouldConsumeAndSneakyThrow() { IOException cause = new IOException("some message"); - // given ThrowingBiConsumer consumer = (i, j) -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingBiConsumer.sneaky(consumer).accept(3, 3)) .hasMessage(cause.getMessage()) .isInstanceOf(IOException.class) diff --git a/src/test/java/com/pivovarit/function/ThrowingBiFunctionTest.java b/src/test/java/com/pivovarit/function/ThrowingBiFunctionTest.java index b9f4d66..c04a907 100644 --- a/src/test/java/com/pivovarit/function/ThrowingBiFunctionTest.java +++ b/src/test/java/com/pivovarit/function/ThrowingBiFunctionTest.java @@ -29,14 +29,12 @@ class ThrowingBiFunctionTest { @Test void shouldThrowEx() throws Exception { - // given Exception exception = new Exception("some message"); ThrowingBiFunction f1 = (i, j) -> { throw exception; }; - // when assertThatThrownBy(() -> f1.apply(42, 42)) .isInstanceOf(exception.getClass()) .hasMessage(exception.getMessage()); @@ -46,10 +44,8 @@ void shouldThrowEx() throws Exception { void shouldWrapInRuntimeExWhenUsingUnchecked() { Exception cause = new Exception("some message"); - // given ThrowingBiFunction f1 = (i, j) -> { throw cause; }; - // when assertThatThrownBy(() -> unchecked(f1).apply(42, 42)).isInstanceOf(CheckedException.class) .hasMessage(cause.getMessage()) .hasCauseInstanceOf(cause.getClass()); @@ -57,37 +53,26 @@ void shouldWrapInRuntimeExWhenUsingUnchecked() { @Test void shouldApplyWhenNoExceptionThrown() { - // given ThrowingBiFunction f1 = (i, j) -> i + j; - // when unchecked(f1).apply(42, 0); - - // then no exception thrown } @Test void shouldWrapInOptionalWhenUsingLifted() { - - // given ThrowingBiFunction f1 = (i, j) -> i + j; - // when Optional result = optional(f1).apply(2, 2); - //then assertThat(result).isPresent(); } @Test void shouldReturnEmptyOptionalWhenUsingOptionalAndExceptionThrown() { - // given ThrowingBiFunction f1 = (i, j) -> { throw new Exception("boom"); }; - // when Optional result = optional(f1).apply(42, 42); - // then assertThat(result).isEmpty(); } @@ -95,7 +80,6 @@ void shouldReturnEmptyOptionalWhenUsingOptionalAndExceptionThrown() { void shouldSneakyThrow() { IOException cause = new IOException("some message"); - // given ThrowingBiFunction f1 = (i, j) -> { throw cause; }; assertThatThrownBy(() -> sneaky(f1).apply(42, 42)) diff --git a/src/test/java/com/pivovarit/function/ThrowingBiPredicateTest.java b/src/test/java/com/pivovarit/function/ThrowingBiPredicateTest.java index cfa5202..c28a8d3 100644 --- a/src/test/java/com/pivovarit/function/ThrowingBiPredicateTest.java +++ b/src/test/java/com/pivovarit/function/ThrowingBiPredicateTest.java @@ -25,13 +25,10 @@ class ThrowingBiPredicateTest { @Test void shouldTest() throws Exception { - // given ThrowingBiPredicate p = (i, j) -> true; - // when boolean result = p.test(42, 0); - // then assertThat(result).isTrue(); } @@ -39,12 +36,10 @@ void shouldTest() throws Exception { void shouldWrapInRuntimeExWhenUsingStandardUtilsFunctions() { Exception cause = new Exception("some message"); - // given ThrowingBiPredicate predicate = (i, j) -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingBiPredicate.unchecked(predicate).test(42, 0)) .isInstanceOf(RuntimeException.class) .hasMessage(cause.getMessage()) @@ -53,13 +48,10 @@ void shouldWrapInRuntimeExWhenUsingStandardUtilsFunctions() { @Test void shouldApplyWhenNoExceptionThrownWithUnchecked() { - // given ThrowingBiPredicate predicate = (i, j) -> i > j; - // when boolean result = ThrowingBiPredicate.unchecked(predicate).test(42, 0); - // then assertThat(result).isTrue(); } @@ -67,10 +59,8 @@ void shouldApplyWhenNoExceptionThrownWithUnchecked() { void shouldSneakyThrow() { IOException cause = new IOException("some message"); - // given ThrowingBiPredicate predicate = (i, j) -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingBiPredicate.sneaky(predicate).test(42, 0)) .isInstanceOf(IOException.class) .hasMessage(cause.getMessage()) diff --git a/src/test/java/com/pivovarit/function/ThrowingBinaryOperatorTest.java b/src/test/java/com/pivovarit/function/ThrowingBinaryOperatorTest.java index 5bfad5f..88c0e9e 100644 --- a/src/test/java/com/pivovarit/function/ThrowingBinaryOperatorTest.java +++ b/src/test/java/com/pivovarit/function/ThrowingBinaryOperatorTest.java @@ -26,11 +26,9 @@ class ThrowingBinaryOperatorTest { @Test void shouldThrowEx() { - // given ThrowingBinaryOperator f1 = (i, j) -> { throw new Exception("some message"); }; - // when assertThatThrownBy(() -> f1.apply(42, 42)) .isInstanceOf(Exception.class) .hasMessage("some message"); @@ -40,11 +38,9 @@ void shouldThrowEx() { void shouldWrapInRuntimeExWhenUsingUnchecked() { Exception cause = new Exception("some message"); - // given ThrowingBinaryOperator f1 = (i, j) -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingBinaryOperator.unchecked(f1).apply(42, 42)) .isInstanceOf(CheckedException.class) .hasMessage(cause.getMessage()) @@ -53,26 +49,18 @@ void shouldWrapInRuntimeExWhenUsingUnchecked() { @Test void shouldApplyWhenNoExceptionThrown() { - // given ThrowingBinaryOperator f1 = Integer::sum; - // when ThrowingBinaryOperator.unchecked(f1).apply(42, 0); - - // then no exception thrown } @Test void shouldWrapInOptionalWhenUsingLifted() { - - // given ThrowingBinaryOperator f1 = Integer::sum; - // when Optional result = ThrowingBiFunction.optional(f1).apply(2, 2); - //then assertThat(result).isPresent(); } @@ -80,10 +68,8 @@ void shouldWrapInOptionalWhenUsingLifted() { void shouldSneakyThrow() { IOException cause = new IOException("some message"); - // given ThrowingBinaryOperator f1 = (i, j) -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingBinaryOperator.sneaky(f1).apply(42, 42)) .isInstanceOf(IOException.class) .hasMessage(cause.getMessage()) diff --git a/src/test/java/com/pivovarit/function/ThrowingBooleanSupplierTest.java b/src/test/java/com/pivovarit/function/ThrowingBooleanSupplierTest.java new file mode 100644 index 0000000..21fa359 --- /dev/null +++ b/src/test/java/com/pivovarit/function/ThrowingBooleanSupplierTest.java @@ -0,0 +1,93 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.io.IOException; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ThrowingBooleanSupplierTest { + + @Test + void shouldGet() throws Exception { + ThrowingBooleanSupplier supplier = () -> true; + assertThat(supplier.getAsBoolean()).isTrue(); + } + + @Test + void shouldReturnValueWhenUncheckedAndNoException() { + ThrowingBooleanSupplier trueSupplier = () -> true; + ThrowingBooleanSupplier falseSupplier = () -> false; + assertThat(ThrowingBooleanSupplier.unchecked(trueSupplier).getAsBoolean()).isTrue(); + assertThat(ThrowingBooleanSupplier.unchecked(falseSupplier).getAsBoolean()).isFalse(); + } + + @Test + void shouldWrapInCheckedException() { + ThrowingBooleanSupplier supplier = () -> { throw new IOException("boom"); }; + assertThatThrownBy(ThrowingBooleanSupplier.unchecked(supplier)::getAsBoolean) + .isInstanceOf(CheckedException.class) + .hasMessage("boom"); + } + + @Test + void shouldReturnValueWhenSneakyAndNoException() { + ThrowingBooleanSupplier trueSupplier = () -> true; + ThrowingBooleanSupplier falseSupplier = () -> false; + assertThat(ThrowingBooleanSupplier.sneaky(trueSupplier).getAsBoolean()).isTrue(); + assertThat(ThrowingBooleanSupplier.sneaky(falseSupplier).getAsBoolean()).isFalse(); + } + + @Test + void shouldSneakyThrow() { + IOException cause = new IOException("io"); + ThrowingBooleanSupplier supplier = () -> { throw cause; }; + assertThatThrownBy(() -> ThrowingBooleanSupplier.sneaky(supplier).getAsBoolean()) + .isInstanceOf(IOException.class) + .hasMessage("io") + .hasNoCause(); + } + + @Test + void shouldRecoverToFalse() { + ThrowingBooleanSupplier supplier = () -> { throw new Exception(); }; + assertThat(ThrowingBooleanSupplier.recover(supplier, e -> false).getAsBoolean()).isFalse(); + } + + @Test + void shouldRecoverToTrue() { + ThrowingBooleanSupplier supplier = () -> { throw new Exception(); }; + assertThat(ThrowingBooleanSupplier.recover(supplier, e -> true).getAsBoolean()).isTrue(); + } + + @Test + void shouldPassThroughOnSuccessfulRecover() { + ThrowingBooleanSupplier trueSupplier = () -> true; + ThrowingBooleanSupplier falseSupplier = () -> false; + assertThat(ThrowingBooleanSupplier.recover(trueSupplier, e -> false).getAsBoolean()).isTrue(); + assertThat(ThrowingBooleanSupplier.recover(falseSupplier, e -> true).getAsBoolean()).isFalse(); + } + + @Test + void shouldRejectNulls() { + assertThatThrownBy(() -> ThrowingBooleanSupplier.unchecked(null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> ThrowingBooleanSupplier.sneaky(null)).isInstanceOf(NullPointerException.class); + ThrowingBooleanSupplier supplier = () -> true; + assertThatThrownBy(() -> ThrowingBooleanSupplier.recover(supplier, null)).isInstanceOf(NullPointerException.class); + } +} diff --git a/src/test/java/com/pivovarit/function/ThrowingConsumerTest.java b/src/test/java/com/pivovarit/function/ThrowingConsumerTest.java index 5e3b867..588a506 100644 --- a/src/test/java/com/pivovarit/function/ThrowingConsumerTest.java +++ b/src/test/java/com/pivovarit/function/ThrowingConsumerTest.java @@ -25,15 +25,12 @@ class ThrowingConsumerTest { @Test void shouldConsume() throws Exception { - // given Integer[] input = {0}; ThrowingConsumer consumer = i -> input[0] = 2; - // when consumer.accept(2); - // then assertThat(input[0]).isEqualTo(2); } @@ -41,10 +38,8 @@ void shouldConsume() throws Exception { void shouldConsumeAndThrowUnchecked() { IOException cause = new IOException("some message"); - // given ThrowingConsumer consumer = i -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingConsumer.unchecked(consumer).accept(3)) .isInstanceOf(CheckedException.class) .hasMessage(cause.getMessage()) @@ -53,23 +48,17 @@ void shouldConsumeAndThrowUnchecked() { @Test void shouldConsumeUnchecked() { - // given ThrowingConsumer consumer = i -> {}; - // when ThrowingConsumer.unchecked(consumer).accept(3); - - // then no exception thrown } @Test void shouldConsumeAndSneakyThrow() { IOException cause = new IOException("some message"); - // given ThrowingConsumer consumer = i -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingConsumer.sneaky(consumer).accept(3)) .isInstanceOf(IOException.class) .hasMessage(cause.getMessage()) diff --git a/src/test/java/com/pivovarit/function/ThrowingDoubleSupplierTest.java b/src/test/java/com/pivovarit/function/ThrowingDoubleSupplierTest.java new file mode 100644 index 0000000..3f5a6af --- /dev/null +++ b/src/test/java/com/pivovarit/function/ThrowingDoubleSupplierTest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.io.IOException; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ThrowingDoubleSupplierTest { + + @Test + void shouldGet() throws Exception { + ThrowingDoubleSupplier supplier = () -> 42.0; + assertThat(supplier.getAsDouble()).isEqualTo(42.0); + } + + @Test + void shouldReturnValueWhenUncheckedAndNoException() { + ThrowingDoubleSupplier supplier = () -> 5.0; + assertThat(ThrowingDoubleSupplier.unchecked(supplier).getAsDouble()).isEqualTo(5.0); + } + + @Test + void shouldWrapInCheckedException() { + ThrowingDoubleSupplier supplier = () -> { throw new IOException("boom"); }; + assertThatThrownBy(ThrowingDoubleSupplier.unchecked(supplier)::getAsDouble) + .isInstanceOf(CheckedException.class) + .hasMessage("boom"); + } + + @Test + void shouldReturnValueWhenSneakyAndNoException() { + ThrowingDoubleSupplier supplier = () -> 5.0; + assertThat(ThrowingDoubleSupplier.sneaky(supplier).getAsDouble()).isEqualTo(5.0); + } + + @Test + void shouldSneakyThrow() { + IOException cause = new IOException("io"); + ThrowingDoubleSupplier supplier = () -> { throw cause; }; + assertThatThrownBy(() -> ThrowingDoubleSupplier.sneaky(supplier).getAsDouble()) + .isInstanceOf(IOException.class) + .hasMessage("io") + .hasNoCause(); + } + + @Test + void shouldRecoverUsingException() { + ThrowingDoubleSupplier supplier = () -> { throw new Exception(); }; + assertThat(ThrowingDoubleSupplier.recover(supplier, e -> -1.0).getAsDouble()).isEqualTo(-1.0); + } + + @Test + void shouldPassThroughOnSuccessfulRecover() { + ThrowingDoubleSupplier supplier = () -> 7.0; + assertThat(ThrowingDoubleSupplier.recover(supplier, e -> -1.0).getAsDouble()).isEqualTo(7.0); + } + + @Test + void shouldRejectNulls() { + assertThatThrownBy(() -> ThrowingDoubleSupplier.unchecked(null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> ThrowingDoubleSupplier.sneaky(null)).isInstanceOf(NullPointerException.class); + ThrowingDoubleSupplier supplier = () -> 1.0; + assertThatThrownBy(() -> ThrowingDoubleSupplier.recover(supplier, null)).isInstanceOf(NullPointerException.class); + } +} diff --git a/src/test/java/com/pivovarit/function/ThrowingFunctionTest.java b/src/test/java/com/pivovarit/function/ThrowingFunctionTest.java index 464c3cd..78a473a 100644 --- a/src/test/java/com/pivovarit/function/ThrowingFunctionTest.java +++ b/src/test/java/com/pivovarit/function/ThrowingFunctionTest.java @@ -31,25 +31,19 @@ class ThrowingFunctionTest { @Test void shouldReturnOptional() { - // given ThrowingFunction f1 = i -> i; - // when - Optional result = f1.lift().apply(42); + Optional result = f1.optional().apply(42); - // then assertThat(result.isPresent()).isTrue(); } @Test void shouldReturnEmptyOptional() { - // given ThrowingFunction f1 = givenThrowingFunction(); - // when - Optional result = f1.lift().apply(42); + Optional result = f1.optional().apply(42); - // then assertThat(result.isPresent()).isFalse(); } @@ -74,26 +68,20 @@ void shouldWrapInRuntimeExWhenUsingUnchecked() { @Test void shouldApplyWhenNoExceptionThrownWithUnchecked() { - // given ThrowingFunction f = i -> i * 2; - // when Integer result = ThrowingFunction.unchecked(f).apply(21); - // then assertThat(result).isEqualTo(42); } @Test void shouldWrapInOptionalWhenUsingStandardUtilsFunctions() { - - // when Long result = Stream.of(". .") - .map(ThrowingFunction.lifted(URI::new)) + .map(ThrowingFunction.optional(URI::new)) .filter(Optional::isPresent) .count(); - //then assertThat(result).isZero(); } @@ -101,10 +89,8 @@ void shouldWrapInOptionalWhenUsingStandardUtilsFunctions() { void shouldSneakyThrow() { IOException cause = new IOException("some message"); - // given ThrowingFunction f = i -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingFunction.sneaky(f).apply(42)) .isInstanceOf(IOException.class) .hasMessage(cause.getMessage()) diff --git a/src/test/java/com/pivovarit/function/ThrowingIntSupplierTest.java b/src/test/java/com/pivovarit/function/ThrowingIntSupplierTest.java new file mode 100644 index 0000000..f861346 --- /dev/null +++ b/src/test/java/com/pivovarit/function/ThrowingIntSupplierTest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.io.IOException; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ThrowingIntSupplierTest { + + @Test + void shouldGet() throws Exception { + ThrowingIntSupplier supplier = () -> 42; + assertThat(supplier.getAsInt()).isEqualTo(42); + } + + @Test + void shouldReturnValueWhenUncheckedAndNoException() { + ThrowingIntSupplier supplier = () -> 5; + assertThat(ThrowingIntSupplier.unchecked(supplier).getAsInt()).isEqualTo(5); + } + + @Test + void shouldWrapInCheckedException() { + ThrowingIntSupplier supplier = () -> { throw new IOException("boom"); }; + assertThatThrownBy(ThrowingIntSupplier.unchecked(supplier)::getAsInt) + .isInstanceOf(CheckedException.class) + .hasMessage("boom"); + } + + @Test + void shouldReturnValueWhenSneakyAndNoException() { + ThrowingIntSupplier supplier = () -> 5; + assertThat(ThrowingIntSupplier.sneaky(supplier).getAsInt()).isEqualTo(5); + } + + @Test + void shouldSneakyThrow() { + IOException cause = new IOException("io"); + ThrowingIntSupplier supplier = () -> { throw cause; }; + assertThatThrownBy(() -> ThrowingIntSupplier.sneaky(supplier).getAsInt()) + .isInstanceOf(IOException.class) + .hasMessage("io") + .hasNoCause(); + } + + @Test + void shouldRecoverUsingException() { + ThrowingIntSupplier supplier = () -> { throw new Exception(); }; + assertThat(ThrowingIntSupplier.recover(supplier, e -> -1).getAsInt()).isEqualTo(-1); + } + + @Test + void shouldPassThroughOnSuccessfulRecover() { + ThrowingIntSupplier supplier = () -> 7; + assertThat(ThrowingIntSupplier.recover(supplier, e -> -1).getAsInt()).isEqualTo(7); + } + + @Test + void shouldRejectNulls() { + assertThatThrownBy(() -> ThrowingIntSupplier.unchecked(null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> ThrowingIntSupplier.sneaky(null)).isInstanceOf(NullPointerException.class); + ThrowingIntSupplier supplier = () -> 1; + assertThatThrownBy(() -> ThrowingIntSupplier.recover(supplier, null)).isInstanceOf(NullPointerException.class); + } +} diff --git a/src/test/java/com/pivovarit/function/ThrowingLongSupplierTest.java b/src/test/java/com/pivovarit/function/ThrowingLongSupplierTest.java new file mode 100644 index 0000000..18ea021 --- /dev/null +++ b/src/test/java/com/pivovarit/function/ThrowingLongSupplierTest.java @@ -0,0 +1,81 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.io.IOException; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ThrowingLongSupplierTest { + + @Test + void shouldGet() throws Exception { + ThrowingLongSupplier supplier = () -> 42L; + assertThat(supplier.getAsLong()).isEqualTo(42L); + } + + @Test + void shouldReturnValueWhenUncheckedAndNoException() { + ThrowingLongSupplier supplier = () -> 5L; + assertThat(ThrowingLongSupplier.unchecked(supplier).getAsLong()).isEqualTo(5L); + } + + @Test + void shouldWrapInCheckedException() { + ThrowingLongSupplier supplier = () -> { throw new IOException("boom"); }; + assertThatThrownBy(ThrowingLongSupplier.unchecked(supplier)::getAsLong) + .isInstanceOf(CheckedException.class) + .hasMessage("boom"); + } + + @Test + void shouldReturnValueWhenSneakyAndNoException() { + ThrowingLongSupplier supplier = () -> 5L; + assertThat(ThrowingLongSupplier.sneaky(supplier).getAsLong()).isEqualTo(5L); + } + + @Test + void shouldSneakyThrow() { + IOException cause = new IOException("io"); + ThrowingLongSupplier supplier = () -> { throw cause; }; + assertThatThrownBy(() -> ThrowingLongSupplier.sneaky(supplier).getAsLong()) + .isInstanceOf(IOException.class) + .hasMessage("io") + .hasNoCause(); + } + + @Test + void shouldRecoverUsingException() { + ThrowingLongSupplier supplier = () -> { throw new Exception(); }; + assertThat(ThrowingLongSupplier.recover(supplier, e -> -1L).getAsLong()).isEqualTo(-1L); + } + + @Test + void shouldPassThroughOnSuccessfulRecover() { + ThrowingLongSupplier supplier = () -> 7L; + assertThat(ThrowingLongSupplier.recover(supplier, e -> -1L).getAsLong()).isEqualTo(7L); + } + + @Test + void shouldRejectNulls() { + assertThatThrownBy(() -> ThrowingLongSupplier.unchecked(null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> ThrowingLongSupplier.sneaky(null)).isInstanceOf(NullPointerException.class); + ThrowingLongSupplier supplier = () -> 1L; + assertThatThrownBy(() -> ThrowingLongSupplier.recover(supplier, null)).isInstanceOf(NullPointerException.class); + } +} diff --git a/src/test/java/com/pivovarit/function/ThrowingPredicateTest.java b/src/test/java/com/pivovarit/function/ThrowingPredicateTest.java index 1c71f6f..c05b71a 100644 --- a/src/test/java/com/pivovarit/function/ThrowingPredicateTest.java +++ b/src/test/java/com/pivovarit/function/ThrowingPredicateTest.java @@ -26,13 +26,10 @@ class ThrowingPredicateTest { @Test void shouldTest() throws Exception { - // given ThrowingPredicate p = i -> true; - // when boolean result = p.test(42); - // then assertThat(result).isTrue(); } @@ -40,12 +37,10 @@ void shouldTest() throws Exception { void shouldWrapInRuntimeExWhenUsingStandardUtilsFunctions() { Exception cause = new Exception("some message"); - // given ThrowingPredicate predicate = i -> { throw cause; }; - // when assertThatThrownBy(() -> Stream.of(42).anyMatch(i -> ThrowingPredicate.unchecked(predicate).test(i))) .isInstanceOf(RuntimeException.class) .hasMessage(cause.getMessage()) @@ -54,13 +49,10 @@ void shouldWrapInRuntimeExWhenUsingStandardUtilsFunctions() { @Test void shouldApplyWhenNoExceptionThrownWithUnchecked() { - // given ThrowingPredicate predicate = i -> i > 0; - // when boolean result = ThrowingPredicate.unchecked(predicate).test(42); - // then assertThat(result).isTrue(); } @@ -68,10 +60,8 @@ void shouldApplyWhenNoExceptionThrownWithUnchecked() { void shouldSneakyThrow() { IOException cause = new IOException("some message"); - // given ThrowingPredicate predicate = i -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingPredicate.sneaky(predicate).test(42)) .isInstanceOf(IOException.class) .hasMessage(cause.getMessage()) diff --git a/src/test/java/com/pivovarit/function/ThrowingRunnableTest.java b/src/test/java/com/pivovarit/function/ThrowingRunnableTest.java index 8eae55c..bcc8d1f 100644 --- a/src/test/java/com/pivovarit/function/ThrowingRunnableTest.java +++ b/src/test/java/com/pivovarit/function/ThrowingRunnableTest.java @@ -25,11 +25,8 @@ class ThrowingRunnableTest { @Test void shouldRun() { - - // given ThrowingRunnable runnable = () -> { throw new IOException("some message"); }; - // when assertThatThrownBy(runnable::run) .isInstanceOf(IOException.class) .hasMessage("some message"); @@ -39,10 +36,8 @@ void shouldRun() { void shouldRunUncheckedAndThrowUsingUtilsMethod() { IOException cause = new IOException("some message"); - // given ThrowingRunnable runnable = () -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingRunnable.unchecked(runnable).run()) .isInstanceOf(CheckedException.class) .hasMessage(cause.getMessage()) @@ -51,14 +46,11 @@ void shouldRunUncheckedAndThrowUsingUtilsMethod() { @Test void shouldRunWhenNoExceptionThrownWithUnchecked() { - // given int[] counter = {0}; ThrowingRunnable runnable = () -> counter[0]++; - // when ThrowingRunnable.unchecked(runnable).run(); - // then assertThat(counter[0]).isEqualTo(1); } @@ -66,10 +58,8 @@ void shouldRunWhenNoExceptionThrownWithUnchecked() { void shouldSneakyThrow() { IOException cause = new IOException("some message"); - // given ThrowingRunnable runnable = () -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingRunnable.sneaky(runnable).run()) .isInstanceOf(IOException.class) .hasMessage(cause.getMessage()) diff --git a/src/test/java/com/pivovarit/function/ThrowingSupplierTest.java b/src/test/java/com/pivovarit/function/ThrowingSupplierTest.java index af4b6f0..f35359e 100644 --- a/src/test/java/com/pivovarit/function/ThrowingSupplierTest.java +++ b/src/test/java/com/pivovarit/function/ThrowingSupplierTest.java @@ -26,13 +26,10 @@ class ThrowingSupplierTest { @Test void shouldGet() throws Exception { - // given ThrowingSupplier supplier = () -> 42; - // when Integer result = supplier.get(); - // then assertThat(result).isEqualTo(42); } @@ -40,7 +37,6 @@ void shouldGet() throws Exception { void shouldGetUncheckedWithUtilsFunction() { IOException cause = new IOException("some message"); - // given ThrowingSupplier supplier = () -> { throw cause; }; assertThatThrownBy(() -> ThrowingSupplier.unchecked(supplier).get()) @@ -51,25 +47,19 @@ void shouldGetUncheckedWithUtilsFunction() { @Test void shouldLiftAndGetWithUtilsFunction() { - // given ThrowingSupplier supplier = () -> 42; - // when Optional result = ThrowingSupplier.optional(supplier).get(); - // then assertThat(result).isPresent(); } @Test void shouldReturnEmptyOptionalWhenUsingOptionalAndExceptionThrown() { - // given ThrowingSupplier supplier = () -> { throw new IOException("boom"); }; - // when Optional result = ThrowingSupplier.optional(supplier).get(); - // then assertThat(result).isEmpty(); } @@ -77,10 +67,8 @@ void shouldReturnEmptyOptionalWhenUsingOptionalAndExceptionThrown() { void shouldSneakyThrow() { IOException cause = new IOException("some message"); - // given ThrowingSupplier supplier = () -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingSupplier.sneaky(supplier).get()) .isInstanceOf(IOException.class) .hasMessage(cause.getMessage()) diff --git a/src/test/java/com/pivovarit/function/ThrowingToDoubleFunctionTest.java b/src/test/java/com/pivovarit/function/ThrowingToDoubleFunctionTest.java new file mode 100644 index 0000000..188b713 --- /dev/null +++ b/src/test/java/com/pivovarit/function/ThrowingToDoubleFunctionTest.java @@ -0,0 +1,70 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.io.IOException; +import java.util.function.ToDoubleFunction; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ThrowingToDoubleFunctionTest { + + @Test + void mapToDoubleTest() { + double result = Stream.of("1.5", "2.5", "3.0") + .mapToDouble(ThrowingToDoubleFunction.unchecked(Double::parseDouble)) + .sum(); + assertThat(result).isEqualTo(7.0); + } + + @Test + void shouldWrapInRuntimeExWhenUsingUnchecked() { + assertThatThrownBy(() -> Stream.of(". .") + .mapToDouble(ThrowingToDoubleFunction.unchecked(Double::parseDouble)) + .sum()) + .isInstanceOf(CheckedException.class) + .hasCauseInstanceOf(NumberFormatException.class); + } + + @Test + void shouldSneakyThrow() { + IOException cause = new IOException("some message"); + ThrowingToDoubleFunction function = s -> { throw cause; }; + + assertThatThrownBy(() -> ThrowingToDoubleFunction.sneaky(function).applyAsDouble("test")) + .isInstanceOf(IOException.class) + .hasMessage(cause.getMessage()) + .hasNoCause(); + } + + @Test + void shouldRecoverWithInputAndException() { + ThrowingToDoubleFunction function = s -> { throw new Exception(s); }; + ToDoubleFunction recovered = ThrowingToDoubleFunction.recover(function, (s, e) -> -1.0); + assertThat(recovered.applyAsDouble("boom")).isEqualTo(-1.0); + } + + @Test + void shouldRejectNullFunction() { + assertThatThrownBy(() -> ThrowingToDoubleFunction.unchecked(null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> ThrowingToDoubleFunction.sneaky(null)).isInstanceOf(NullPointerException.class); + ThrowingToDoubleFunction function = s -> 1.0; + assertThatThrownBy(() -> ThrowingToDoubleFunction.recover(function, null)).isInstanceOf(NullPointerException.class); + } +} diff --git a/src/test/java/com/pivovarit/function/ThrowingToIntFunctionTest.java b/src/test/java/com/pivovarit/function/ThrowingToIntFunctionTest.java new file mode 100644 index 0000000..8947c80 --- /dev/null +++ b/src/test/java/com/pivovarit/function/ThrowingToIntFunctionTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2014-2026 Grzegorz Piwowarek, https://4comprehension.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pivovarit.function; + +import java.io.IOException; +import java.util.function.ToIntFunction; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ThrowingToIntFunctionTest { + + @Test + void mapToIntTest() { + int result = Stream.of("1", "2", "3") + .mapToInt(ThrowingToIntFunction.unchecked(Integer::parseInt)) + .sum(); + assertThat(result).isEqualTo(6); + } + + @Test + void shouldWrapInRuntimeExWhenUsingUnchecked() { + assertThatThrownBy(() -> Stream.of(". .") + .mapToInt(ThrowingToIntFunction.unchecked(Integer::parseInt)) + .sum()) + .isInstanceOf(CheckedException.class) + .hasMessage("For input string: \". .\"") + .hasCauseInstanceOf(NumberFormatException.class); + } + + @Test + void shouldSneakyThrow() { + IOException cause = new IOException("some message"); + ThrowingToIntFunction function = s -> { throw cause; }; + + assertThatThrownBy(() -> ThrowingToIntFunction.sneaky(function).applyAsInt("test")) + .isInstanceOf(IOException.class) + .hasMessage(cause.getMessage()) + .hasNoCause(); + } + + @Test + void shouldRecoverWithInputAndException() { + ThrowingToIntFunction function = s -> { throw new Exception(s); }; + ToIntFunction recovered = ThrowingToIntFunction.recover(function, (s, e) -> s.length()); + assertThat(recovered.applyAsInt("boom")).isEqualTo(4); + } + + @Test + void shouldRejectNullFunction() { + assertThatThrownBy(() -> ThrowingToIntFunction.unchecked(null)).isInstanceOf(NullPointerException.class); + assertThatThrownBy(() -> ThrowingToIntFunction.sneaky(null)).isInstanceOf(NullPointerException.class); + ThrowingToIntFunction function = s -> 1; + assertThatThrownBy(() -> ThrowingToIntFunction.recover(function, null)).isInstanceOf(NullPointerException.class); + } +} diff --git a/src/test/java/com/pivovarit/function/ThrowingToLongFunctionTest.java b/src/test/java/com/pivovarit/function/ThrowingToLongFunctionTest.java index 6c65f1d..3733bfc 100644 --- a/src/test/java/com/pivovarit/function/ThrowingToLongFunctionTest.java +++ b/src/test/java/com/pivovarit/function/ThrowingToLongFunctionTest.java @@ -47,10 +47,8 @@ void shouldWrapInRuntimeExWhenUsingUnchecked() { void shouldSneakyThrow() { IOException cause = new IOException("some message"); - // given ThrowingToLongFunction function = s -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingToLongFunction.sneaky(function).applyAsLong("test")) .isInstanceOf(IOException.class) .hasMessage(cause.getMessage()) diff --git a/src/test/java/com/pivovarit/function/ThrowingUnaryOperatorTest.java b/src/test/java/com/pivovarit/function/ThrowingUnaryOperatorTest.java index 0bf2e10..18cf039 100644 --- a/src/test/java/com/pivovarit/function/ThrowingUnaryOperatorTest.java +++ b/src/test/java/com/pivovarit/function/ThrowingUnaryOperatorTest.java @@ -24,31 +24,22 @@ class ThrowingUnaryOperatorTest { @Test void shouldApply() throws Exception { - // given ThrowingUnaryOperator op = i -> i; - // when op.apply(42); - - // then no exception thrown } @Test void shouldApplyUnchecked() { - // given ThrowingUnaryOperator op = i -> i; - // when ThrowingUnaryOperator.unchecked(op).apply(42); - - // then no exception thrown } @Test void shouldApplyUncheckedAndThrow() { final IOException cause = new IOException("some message"); - // given ThrowingUnaryOperator op = i -> { throw cause; }; assertThatThrownBy(() -> ThrowingUnaryOperator.unchecked(op).apply(42)) @@ -59,7 +50,6 @@ void shouldApplyUncheckedAndThrow() { @Test void shouldApplyUncheckedAndThrowNPE() { - // when assertThatThrownBy(() -> ThrowingUnaryOperator.unchecked(null).apply(42)) .isInstanceOf(NullPointerException.class); } @@ -68,10 +58,8 @@ void shouldApplyUncheckedAndThrowNPE() { void shouldSneakyThrow() { IOException cause = new IOException("some message"); - // given ThrowingUnaryOperator op = i -> { throw cause; }; - // when assertThatThrownBy(() -> ThrowingUnaryOperator.sneaky(op).apply(42)) .isInstanceOf(IOException.class) .hasMessage(cause.getMessage())