How can I throw CHECKED exceptions from inside Java 8 streams?

前端 未结 18 1608
你的背包
你的背包 2020-11-22 06:59

How can I throw CHECKED exceptions from inside Java 8 streams/lambdas?

In other words, I want to make code like this compile:

public List

        
18条回答
  •  遇见更好的自我
    2020-11-22 07:31

    Summarizing the comments above the advanced solution is to use a special wrapper for unchecked functions with builder like API which provides recovering, rethrowing and suppresing.

    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
              .map(Try.>safe(Class::forName)
                      .handle(System.out::println)
                      .unsafe())
              .collect(toList());
    

    Code below demonstrates it for Consumer, Supplier and Function interfaces. It can be easly expanded. Some public keywords were removed for this example.

    Class Try is the endpoint for client code. Safe methods may have unique name for each function type. CheckedConsumer, CheckedSupplier and CheckedFunction are checked analogs of lib functions which can be used independently of Try

    CheckedBuilder is the interface for handling exceptions in some checked function. orTry allows execute another same type function if previous was failed. handle provides exception handling including exception type filtering. The order of handlers is important. Reduce methods unsafe and rethrow rethrows last exception in the execution chain. Reduce methods orElse and orElseGet return alternate value like Optional ones if all functions failed. Also there is method suppress. CheckedWrapper is the common implementation of CheckedBuilder.

    final class Try {
    
        public static  CheckedBuilder, CheckedSupplier, T> 
            safe(CheckedSupplier supplier) {
            return new CheckedWrapper<>(supplier, 
                    (current, next, handler, orResult) -> () -> {
                try { return current.get(); } catch (Exception ex) {
                    handler.accept(ex);
                    return next.isPresent() ? next.get().get() : orResult.apply(ex);
                }
            });
        }
    
        public static  Supplier unsafe(CheckedSupplier supplier) {
            return supplier;
        }
    
        public static  CheckedBuilder, CheckedConsumer, Void> 
            safe(CheckedConsumer consumer) {
            return new CheckedWrapper<>(consumer, 
                    (current, next, handler, orResult) -> t -> {
                try { current.accept(t); } catch (Exception ex) {
                    handler.accept(ex);
                    if (next.isPresent()) {
                        next.get().accept(t);
                    } else {
                        orResult.apply(ex);
                    }
                }
            });
        }
    
        public static  Consumer unsafe(CheckedConsumer consumer) {
            return consumer;
        }
    
        public static  CheckedBuilder, CheckedFunction, R> 
            safe(CheckedFunction function) {
            return new CheckedWrapper<>(function, 
                    (current, next, handler, orResult) -> t -> {
                try { return current.applyUnsafe(t); } catch (Exception ex) {
                    handler.accept(ex);
                    return next.isPresent() ? next.get().apply(t) : orResult.apply(ex);
                }
            });
        }
    
        public static  Function unsafe(CheckedFunction function) {
            return function;
        }
    
        @SuppressWarnings ("unchecked")
        static  T throwAsUnchecked(Throwable exception) throws E { 
            throw (E) exception; 
        }
    }
    
    @FunctionalInterface interface CheckedConsumer extends Consumer {
        void acceptUnsafe(T t) throws Exception;
        @Override default void accept(T t) {
            try { acceptUnsafe(t); } catch (Exception ex) {
                Try.throwAsUnchecked(ex);
            }
        }
    }
    
    @FunctionalInterface interface CheckedFunction extends Function {
        R applyUnsafe(T t) throws Exception;
        @Override default R apply(T t) {
            try { return applyUnsafe(t); } catch (Exception ex) {
                return Try.throwAsUnchecked(ex);
            }
        }
    }
    
    @FunctionalInterface interface CheckedSupplier extends Supplier {
        T getUnsafe() throws Exception;
        @Override default T get() {
            try { return getUnsafe(); } catch (Exception ex) {
                return Try.throwAsUnchecked(ex);
            }
        }
    }
    
    interface ReduceFunction {
        TSafe wrap(TUnsafe current, Optional next, 
                Consumer handler, Function orResult);
    }
    
    interface CheckedBuilder {
        CheckedBuilder orTry(TUnsafe next);
    
        CheckedBuilder handle(Consumer handler);
    
         CheckedBuilder handle(
                Class exceptionType, Consumer handler);
    
        CheckedBuilder handleLast(Consumer handler);
    
         CheckedBuilder handleLast(
                Class exceptionType, Consumer handler);
    
        TSafe unsafe();
        TSafe rethrow(Function transformer);
        TSafe suppress();
        TSafe orElse(R value);
        TSafe orElseGet(Supplier valueProvider);
    }
    
    final class CheckedWrapper 
            implements CheckedBuilder {
    
        private final TUnsafe function;
        private final ReduceFunction reduceFunction;
    
        private final CheckedWrapper root;
        private CheckedWrapper next;
    
        private Consumer handlers = ex -> { };
        private Consumer lastHandlers = ex -> { };
    
        CheckedWrapper(TUnsafe function, 
                ReduceFunction reduceFunction) {
            this.function = function;
            this.reduceFunction = reduceFunction;
            this.root = this;
        }
    
        private CheckedWrapper(TUnsafe function, 
                CheckedWrapper prev) {
            this.function = function;
            this.reduceFunction = prev.reduceFunction;
            this.root = prev.root;
            prev.next = this;
        }
    
        @Override public CheckedBuilder orTry(TUnsafe next) {
            return new CheckedWrapper<>(next, this);
        }
    
        @Override public CheckedBuilder handle(
                Consumer handler) {
            handlers = handlers.andThen(handler);
            return this;
        }
    
        @Override public  CheckedBuilder 
            handle(Class exceptionType, Consumer handler) {
            handlers = handlers.andThen(ex -> {
                if (exceptionType.isInstance(ex)) {
                    handler.accept(exceptionType.cast(ex));
                }
            });
            return this;
        }
    
        @Override public CheckedBuilder handleLast(
                Consumer handler) {
            lastHandlers = lastHandlers.andThen(handler);
            return this;
        }
    
        @Override public  CheckedBuilder 
            handleLast(Class exceptionType, Consumer handler) {
            lastHandlers = lastHandlers.andThen(ex -> {
                if (exceptionType.isInstance(ex)) {
                    handler.accept(exceptionType.cast(ex));
                }
            });
            return this;
        }
    
        @Override public TSafe unsafe() {
            return root.reduce(ex -> Try.throwAsUnchecked(ex));
        }
    
        @Override
        public TSafe rethrow(Function transformer) {
            return root.reduce(ex -> Try.throwAsUnchecked(transformer.apply(ex)));
        }
    
        @Override public TSafe suppress() {
            return root.reduce(ex -> null);
        }
    
        @Override public TSafe orElse(R value) {
            return root.reduce(ex -> value);
        }
    
        @Override public TSafe orElseGet(Supplier valueProvider) {
            Objects.requireNonNull(valueProvider);
            return root.reduce(ex -> valueProvider.get());
        }
    
        private TSafe reduce(Function orResult) {
            return reduceFunction.wrap(function, 
                    Optional.ofNullable(next).map(p -> p.reduce(orResult)), 
                    this::handle, orResult);
        }
    
        private void handle(Throwable ex) {
            for (CheckedWrapper current = this; 
                    current != null; 
                    current = current.next) {
                current.handlers.accept(ex);
            }
            lastHandlers.accept(ex);
        }
    }
    

提交回复
热议问题