GithubHelp home page GithubHelp logo

hgwood / java8-streams-and-exceptions Goto Github PK

View Code? Open in Web Editor NEW
35.0 6.0 7.0 28 KB

Experiments with dealing with exceptions while using the Stream API

License: MIT License

Java 100.00%
java streams exceptions

java8-streams-and-exceptions's Introduction

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.

java8-streams-and-exceptions's People

Contributors

hgwood avatar vp-hwood avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

java8-streams-and-exceptions's Issues

Please add a license to this project

Hello,

I found your project and I think the concepts are really useful. Could you add a license to the repository so its clear if we could use your code directly, or as a library, etc?

Do you know of other similar implementations?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.