Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -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<R, E>` 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<T, E>` (`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<Integer, R>` 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`).
73 changes: 67 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -57,12 +57,25 @@ stream.map(ThrowingFunction.unchecked(URI::new))
| `ThrowingPredicate<T, E>` | `boolean test(T t) throws E` |
| `ThrowingBiPredicate<T, U, E>` | `boolean test(T t, U u) throws E` |
| `ThrowingRunnable<E>` | `void run() throws E` |
| `ThrowingToIntFunction<T, E>` | `int applyAsInt(T t) throws E` |
| `ThrowingToLongFunction<T, E>` | `long applyAsLong(T t) throws E` |
| `ThrowingToDoubleFunction<T, E>` | `double applyAsDouble(T t) throws E` |
| `ThrowingIntSupplier<E>` | `int getAsInt() throws E` |
| `ThrowingLongSupplier<E>` | `long getAsLong() throws E` |
| `ThrowingDoubleSupplier<E>` | `double getAsDouble() throws E` |
| `ThrowingBooleanSupplier<E>` | `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.
Expand All @@ -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<T, Optional<R>>`
- `ThrowingFunction.optional(f)` → `Function<T, Optional<R>>`
- `ThrowingBiFunction.optional(f)` → `BiFunction<T1, T2, Optional<R>>`
- `ThrowingSupplier.optional(s)` → `Supplier<Optional<T>>`

```java
stream.map(ThrowingFunction.lifted(URI::new)) // Stream<Optional<URI>>
stream.map(ThrowingFunction.optional(URI::new)) // Stream<Optional<URI>>
.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
Expand All @@ -102,16 +130,49 @@ stream.map(ThrowingFunction.lifted(URI::new)) // Stream<Optional<URI>>
<dependency>
<groupId>com.pivovarit</groupId>
<artifactId>throwing-function</artifactId>
<version>1.6.1</version>
<version>2.0.0</version>
</dependency>
```

### 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<byte[]> 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).
34 changes: 29 additions & 5 deletions src/main/java/com/pivovarit/function/ThrowingBiConsumer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.pivovarit.function;

import java.util.function.BiConsumer;
import java.util.function.Consumer;

import static java.util.Objects.requireNonNull;

Expand All @@ -27,22 +28,22 @@
*
* @param <T1> the type of the first argument to the operation
* @param <T2> the type of the second argument to the operation
* @param <EX> the type of the thrown checked exception
* @param <E> the type of the thrown checked exception
*
* @author Grzegorz Piwowarek
* @see ThrowingConsumer
*/
@FunctionalInterface
public interface ThrowingBiConsumer<T1, T2, EX extends Exception> {
public interface ThrowingBiConsumer<T1, T2, E extends Exception> {

/**
* 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}
Expand Down Expand Up @@ -81,4 +82,27 @@ static <T, U> BiConsumer<T, U> sneaky(ThrowingBiConsumer<? super T, ? super U, ?
}
};
}

/**
* Returns a new BiConsumer instance which, in case of a thrown checked exception, passes the thrown
* exception to the supplied handler
*
* @param <T> the type of the first argument to the operation
* @param <U> 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 <T, U> BiConsumer<T, U> recover(ThrowingBiConsumer<? super T, ? super U, ?> consumer, Consumer<? super Exception> handler) {
requireNonNull(consumer);
requireNonNull(handler);
return (arg1, arg2) -> {
try {
consumer.accept(arg1, arg2);
} catch (final Exception e) {
handler.accept(e);
}
};
}
}
25 changes: 25 additions & 0 deletions src/main/java/com/pivovarit/function/ThrowingBiFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;

import static java.util.Objects.requireNonNull;

Expand Down Expand Up @@ -106,4 +107,28 @@ static <T1, T2, R> BiFunction<T1, T2, Optional<R>> optional(ThrowingBiFunction<?
}
};
}

/**
* Returns a new BiFunction instance which, in case of a thrown checked exception, returns the result
* produced by the supplied handler applied to the thrown exception
*
* @param <T1> the type of the first argument to the function
* @param <T2> the type of the second argument to the function
* @param <R> 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 <T1, T2, R> BiFunction<T1, T2, R> recover(ThrowingBiFunction<? super T1, ? super T2, ? extends R, ?> function, Function<? super Exception, ? extends R> handler) {
requireNonNull(function);
requireNonNull(handler);
return (arg1, arg2) -> {
try {
return function.apply(arg1, arg2);
} catch (final Exception e) {
return handler.apply(e);
}
};
}
}
24 changes: 24 additions & 0 deletions src/main/java/com/pivovarit/function/ThrowingBiPredicate.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.Objects;
import java.util.function.BiPredicate;
import java.util.function.Predicate;

import static java.util.Objects.requireNonNull;

Expand Down Expand Up @@ -80,4 +81,27 @@ static <T, U> BiPredicate<T, U> sneaky(ThrowingBiPredicate<? super T, ? super U,
}
};
}

/**
* Returns a new BiPredicate instance which, in case of a thrown checked exception, returns the result
* produced by the supplied handler applied to the thrown exception
*
* @param <T> the type of the first argument to the predicate
* @param <U> 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 <T, U> BiPredicate<T, U> recover(ThrowingBiPredicate<? super T, ? super U, ?> predicate, Predicate<? super Exception> handler) {
requireNonNull(predicate);
requireNonNull(handler);
return (arg1, arg2) -> {
try {
return predicate.test(arg1, arg2);
} catch (final Exception e) {
return handler.test(e);
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.pivovarit.function;

import java.util.function.BinaryOperator;
import java.util.function.Function;

import static java.util.Objects.requireNonNull;

Expand Down Expand Up @@ -72,4 +73,26 @@ static <T> BinaryOperator<T> sneaky(ThrowingBinaryOperator<T, ?> 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 <T> 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 <T> BinaryOperator<T> recover(ThrowingBinaryOperator<T, ?> operator, Function<? super Exception, ? extends T> handler) {
requireNonNull(operator);
requireNonNull(handler);
return (t1, t2) -> {
try {
return operator.apply(t1, t2);
} catch (final Exception e) {
return handler.apply(e);
}
};
}
}
Loading