All Projects → hgwood → java8-streams-and-exceptions

hgwood / java8-streams-and-exceptions

Licence: MIT license
Experiments with dealing with exceptions while using the Stream API

Programming Languages

java
68154 projects - #9 most used programming language

Projects that are alternatives of or similar to java8-streams-and-exceptions

streamplify
Java 8 combinatorics-related streams and other utilities
Stars: ✭ 40 (+11.11%)
Mutual labels:  streams
async-stream-generator
Pipe ES6 Async Generators through Node.js Streams
Stars: ✭ 48 (+33.33%)
Mutual labels:  streams
ExceptionCatcher
Catch Objective-C exceptions in Swift
Stars: ✭ 97 (+169.44%)
Mutual labels:  exceptions
bugsnag-java
Bugsnag error reporting for Java.
Stars: ✭ 51 (+41.67%)
Mutual labels:  exceptions
django-http-exceptions
HTTP exceptions for django
Stars: ✭ 29 (-19.44%)
Mutual labels:  exceptions
stack-trace-art
Turning programming exceptions into art
Stars: ✭ 39 (+8.33%)
Mutual labels:  exceptions
logstreamer
Prefixes streams (e.g. stdout or stderr) in Go
Stars: ✭ 41 (+13.89%)
Mutual labels:  streams
vcrtl
C++ Exceptions in Windows Drivers
Stars: ✭ 141 (+291.67%)
Mutual labels:  exceptions
JavaSE8-Features
Take a tour of the new features in Java SE 8, the platform designed to support faster and easier Java development. Learn about Project Lambda, a new syntax to support lambda expressions in Java code; the new Stream API for processing collections and managing parallel processing; the DateTime API for representing, managing and calculating date an…
Stars: ✭ 51 (+41.67%)
Mutual labels:  streams
exceptions-java
Curso Tratamento de Exceções em Java
Stars: ✭ 489 (+1258.33%)
Mutual labels:  exceptions
Coderr.Client
Core client library for Coderr
Stars: ✭ 23 (-36.11%)
Mutual labels:  exceptions
FPChecker
A dynamic analysis tool to detect floating-point errors in HPC applications.
Stars: ✭ 26 (-27.78%)
Mutual labels:  exceptions
streamalg
Extensible stream pipelines with object algebras.
Stars: ✭ 26 (-27.78%)
Mutual labels:  streams
ng-observe
Angular reactivity streamlined...
Stars: ✭ 65 (+80.56%)
Mutual labels:  streams
stream-list-updater
Automation for updating an index of live George Floyd protest streams
Stars: ✭ 15 (-58.33%)
Mutual labels:  streams
quick-csv-streamer
Quick CSV Parser with Java 8 Streams API
Stars: ✭ 29 (-19.44%)
Mutual labels:  streams
rollbar-java
Rollbar for Java and Android
Stars: ✭ 71 (+97.22%)
Mutual labels:  exceptions
muxrpc
lightweight multiplexed rpc
Stars: ✭ 96 (+166.67%)
Mutual labels:  streams
compressstream-explainer
Compression Streams Explained
Stars: ✭ 22 (-38.89%)
Mutual labels:  streams
easybuggy4django
EasyBuggy clone built on Django
Stars: ✭ 44 (+22.22%)
Mutual labels:  exceptions

Java 8's new Stream API, being FP-inspired, doesn't play with exceptions very well.

For example, let's say we want to map a set of URI strings to URI objects:

uriStrings.stream().map(URI::create).collect(toList());

If one of the strings is not a valid URL, map() still runs OK because it does nothing but register the map operation, however, collect() does call URI::create, which throws an IllegalArgumentException and the overall operation fails without hope of retrieving the URIs that were valid. A custom collector cannot solve this issue because collect() computes the value before it hands it to the collector.

This is my attempt to solve this problem. The idea is that the collector has to be able to control when the exception-throwing function is called in order to catch exceptions and process them, so the call has to be delayed. To achieve this, values are mapped to supplier of values using a method I called lazy:

public static <T, R> Function<T, Supplier<R>> lazy(Function<T, R> f) {
    return input -> () -> f.apply(input);
}

Notice that f.apply() is never called inside lazy, nor is it called when the returned function is called. Mapping a lazy()-wrapped A to B function onto a stream of As results in a stream of suppliers of Bs. A downstream collector therefore gets to work with suppliers instead of values (URIs in the example). The collector can then choose to call (or not call) Supplier::get and properly catch exceptions thrown by it.

Examples

All the following examples take this data as input. The first and third strings are valid URIs: URI::create parses them OK. The second one make the same method choke and throw an IllegalArgumentException.

Collection<String> data = asList("http://elevated", "invalid\nurl", "http://abstractions");

Let's review different ways to manage a map operation of URI::create on this data.

Discarding failures

The discarding collector swallows exceptions of specified types. Exception types have to be explicitly passed in order to propagate other exceptions. The following code results in a stream containing "http://elevated" and "http://abstractions".

data.stream()
    .map(lazy(URI::create))
    .collect(discarding(IllegalArgumentException.class));

Silently stopping the computation on failure, returning partial results

The upTo collector also swallows exception, but it does not continue to read the input once the first exception is thrown. The following code results in a stream containing "http://elevated".

data.stream()
    .map(lazy(URI::create))
    .collect(upTo(IllegalArgumentException.class));

Throwing on failure, keeping partial results

The upToAndThrow collector wraps the first exception (of specified types) it encounters in a FailFastCollectException. The interesting bit is that the FailFastCollectException gives you access to the results that were successfully computed prior to the failure. In the following code, e.getResults() returns a stream containing "http://elevated".

try {
    data.stream()
        .map(lazy(URI::create))
        .collect(upToAndThrow(IllegalArgumentException.class));
} catch (FailFastCollectException e) {
    e.getCause();
    e.getResults();
}

Throwing at the end, keeping partial results

The throwingAtEnd collector reads the whole input, collecting both results and exceptions as it goes. Once the stream is fully read, it throws a CollectException that gives access to what it's been collecting. In the following code, e.getResults() returns a stream containing "http://elevated" and "http://abstractions" while e.getCauses() return a collection containing an IllegalArgumentException instance.

try {
    data.stream()
        .map(lazy(URI::create))
        .collect(throwingAtEnd(IllegalArgumentException.class));
} catch (CollectException e) {
    e.getCauses();
    e.getResults();
}

Dealing With Checked Exceptions

This method can also be used with checked exceptions, if they are first wrapped into unchecked ones.

data.stream()
    .map(lazy(sneaky(URI::new, e -> new CustomRuntimeException(e))))
    .collect(discarding(CustomRuntimeException.class));

The above code is simplified if the library provides a default wrapping.

data.stream()
    .map(lazy(sneaky(URI::new)))
    .collect(discardingFailures());

Acknowledgement

The motivation for this experiment was triggered by the work of Yohan Legat, a co-worker at Zenika. His work is also on GitHub and he wrote a article (in French) on Zenika's technical blog.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].