top | item 8380710

(no title)

steve_barham | 11 years ago

Regarding the 'checked exception issue'; an alternative technique to the method-wrapping-lambda is to use a default interface method to implement the non-exceptional interface, which delegates to the possibly-exceptional method. It's a bit fussy, but it's a nice technique to know:

    interface IOConsumer<T> extends Consumer<T> {
        @Override
        default void accept(T t) {
            try { 
                maybeAccept(t); 
            } catch (IOException ie) { 
                throw new RuntimeException(ie); }
            }

        void maybeAccept(T t) throws IOException;
    }
As IOConsumer<T> is also a Consumer<T>, if you can assign the lambda to an IOConsumer<T> it will get this wrapping behaviour automatically. So your code might look something like:

        try (Writer writer = new StringWriter()) {
            final IOConsumer<String> ioPrinter = writer::write;

            // hand ioPrinter off to something that expects a Consumer<String>
        }
This is by no means beautiful; for 'writer::write' to be treated as an IOConsumer<T> requires that the target type be IOConsumer<T>, so typically would require an interim assignment. It does allow us to simplify the 'wrapping' method, though, so that lambdas can again be used as one-liners:

    static <T> Consumer<T> maybe(IOConsumer<T> consumer) {
        return consumer;
    }

    try (Writer writer = new StringWriter()) {
        final Consumer<String> printer = maybe(writer::write);
    }
It would be nice not to have these wrinkles, but I think Java 8 has done a fairly decent job of preserving backwards compatibility while introducing language features and APIs which feel like a vast improvement. I'd love to see reified generics in the future, particularly if the type parameter can be extended to support primitives.

discuss

order

BrandonM|11 years ago

We currently use the following pattern:

    interface ConsumerE<T, E extends Exception> {
        void accept(T t) throws E;
    }
This actually works! The type inference is good enough to figure out the actual type of E.

Note that Java 8 also includes specialized wrappers like RuntimeIOException. So it might be nice to extend your example:

    static <T, E extends Exception> Consumer<T> wrapped(
            Function<? super E, RuntimeException> wrapper,
            ConsumerE<T, E> consumer) {
        return (t) -> {
            try {
                consumer.accept(t);
            } catch (RuntimeException e) {
                throw e;
            } catch (Exception e) {
                throw wrapper.apply((E) e);
            }
        };
    }
Used as follows:

    Consumer<String> printer = wrapped(RuntimeIOException::new, writer::write);

phernandez|11 years ago

I was trying to work around this problem also and found this library: https://github.com/jOOQ/jOOL, from the guys at Jooq. They did all the hard work to wrap all the FunctionalInterfaces in unchecked exceptions. Hope this helps.