Java 8 Lambda function that throws exception?

后端 未结 26 1680
臣服心动
臣服心动 2020-11-22 03:14

I know how to create a reference to a method that has a String parameter and returns an int, it\'s:

Function         


        
相关标签:
26条回答
  • 2020-11-22 03:33

    You'll need to do one of the following.

    • If it's your code, then define your own functional interface that declares the checked exception:

      @FunctionalInterface
      public interface CheckedFunction<T, R> {
         R apply(T t) throws IOException;
      }
      

      and use it:

      void foo (CheckedFunction f) { ... }
      
    • Otherwise, wrap Integer myMethod(String s) in a method that doesn't declare a checked exception:

      public Integer myWrappedMethod(String s) {
          try {
              return myMethod(s);
          }
          catch(IOException e) {
              throw new UncheckedIOException(e);
          }
      }
      

      and then:

      Function<String, Integer> f = (String t) -> myWrappedMethod(t);
      

      or:

      Function<String, Integer> f =
          (String t) -> {
              try {
                 return myMethod(t);
              }
              catch(IOException e) {
                  throw new UncheckedIOException(e);
              }
          };
      
    0 讨论(0)
  • 2020-11-22 03:34

    Create a custom return type that will propagate the checked exception. This is an alternative to creating a new interface that mirrors the existing functional interface with the slight modification of a "throws exception" on the functional interface's method.

    Definition

    CheckedValueSupplier

    public static interface CheckedValueSupplier<V> {
        public V get () throws Exception;
    }
    

    CheckedValue

    public class CheckedValue<V> {
        private final V v;
        private final Optional<Exception> opt;
    
        public Value (V v) {
            this.v = v;
        }
    
        public Value (Exception e) {
            this.opt = Optional.of(e);
        }
    
        public V get () throws Exception {
            if (opt.isPresent()) {
                throw opt.get();
            }
            return v;
        }
    
        public Optional<Exception> getException () {
            return opt;
        }
    
        public static <T> CheckedValue<T> returns (T t) {
            return new CheckedValue<T>(t);
        }
    
        public static <T> CheckedValue<T> rethrows (Exception e) {
            return new CheckedValue<T>(e);
        }
    
        public static <V> CheckedValue<V> from (CheckedValueSupplier<V> sup) {
            try {
                return CheckedValue.returns(sup.get());
            } catch (Exception e) {
                return Result.rethrows(e);
            }
        }
    
        public static <V> CheckedValue<V> escalates (CheckedValueSupplier<V> sup) {
            try {
                return CheckedValue.returns(sup.get());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
    }
    

    Usage

    //  Don't use this pattern with FileReader, it's meant to be an
    //  example.  FileReader is a Closeable resource and as such should
    //  be managed in a try-with-resources block or in another safe
    //  manner that will make sure it is closed properly.
    
    //  This will not compile as the FileReader constructor throws
    //  an IOException.
        Function<String, FileReader> sToFr =
            (fn) -> new FileReader(Paths.get(fn).toFile());
    
    // Alternative, this will compile.
        Function<String, CheckedValue<FileReader>> sToFr = (fn) -> {
            return CheckedValue.from (
                () -> new FileReader(Paths.get("/home/" + f).toFile()));
        };
    
    // Single record usage
        // The call to get() will propagate the checked exception if it exists.
        FileReader readMe = pToFr.apply("/home/README").get();
    
    
    // List of records usage
        List<String> paths = ...; //a list of paths to files
        Collection<CheckedValue<FileReader>> frs =
            paths.stream().map(pToFr).collect(Collectors.toList());
    
    // Find out if creation of a file reader failed.
        boolean anyErrors = frs.stream()
            .filter(f -> f.getException().isPresent())
            .findAny().isPresent();
    

    What's going on?

    A single functional interface that throws a checked exception is created (CheckedValueSupplier). This will be the only functional interface which allows checked exceptions. All other functional interfaces will leverage the CheckedValueSupplier to wrap any code that throws a checked exception.

    The CheckedValue class will hold the result of executing any logic that throws a checked exception. This prevents propagation of a checked exception until the point at which code attempts to access the value that an instance of CheckedValue contains.

    The problems with this approach.

    • We are now throwing "Exception" effectively hiding the specific type originally thrown.
    • We are unaware that an exception occurred until CheckedValue#get() is called.

    Consumer et al

    Some functional interfaces (Consumer for example) must be handled in a different manner as they don't provide a return value.

    Function in lieu of Consumer

    One approach is to use a function instead of a consumer, which applies when handling streams.

        List<String> lst = Lists.newArrayList();
    // won't compile
    lst.stream().forEach(e -> throwyMethod(e));
    // compiles
    lst.stream()
        .map(e -> CheckedValueSupplier.from(
            () -> {throwyMethod(e); return e;}))
        .filter(v -> v.getException().isPresent()); //this example may not actually run due to lazy stream behavior
    

    Escalate

    Alternatively, you can always escalate to a RuntimeException. There are other answers that cover escalation of a checked exception from within a Consumer.

    Don't consume.

    Just avoid functional interfaces all together and use a good-ole-fashioned for loop.

    0 讨论(0)
  • 2020-11-22 03:35

    If you have lombok, you can annotate your method with @SneakyThrows

    SneakyThrow does not silently swallow, wrap into RuntimeException, or otherwise modify any exceptions of the listed checked exception types. The JVM does not check for the consistency of the checked exception system; javac does, and this annotation lets you opt out of its mechanism.

    https://projectlombok.org/features/SneakyThrows

    0 讨论(0)
  • 2020-11-22 03:40

    Several of the offered solutions use a generic argument of E to pass in the type of the exception which gets thrown.

    Take that one step further, and rather than passing in the type of the exception, pass in a Consumer of the type of exception, as in...

    Consumer<E extends Exception>
    

    You might create several re-usable variations of Consumer<Exception> which would cover the common exception handling needs of your application.

    0 讨论(0)
  • 2020-11-22 03:42

    This is not specific to Java 8. You are trying to compile something equivalent to:

    interface I {
        void m();
    }
    class C implements I {
        public void m() throws Exception {} //can't compile
    }
    
    0 讨论(0)
  • 2020-11-22 03:42

    Another solution using a Function wrapper would be to return either an instance of a wrapper of your result, say Success, if everything went well, either an instance of, say Failure.

    Some code to clarify things :

    public interface ThrowableFunction<A, B> {
        B apply(A a) throws Exception;
    }
    
    public abstract class Try<A> {
    
        public static boolean isSuccess(Try tryy) {
            return tryy instanceof Success;
        }
    
        public static <A, B> Function<A, Try<B>> tryOf(ThrowableFunction<A, B> function) {
            return a -> {
                try {
                    B result = function.apply(a);
                    return new Success<B>(result);
                } catch (Exception e) {
                    return new Failure<>(e);
                }
            };
        }
    
        public abstract boolean isSuccess();
    
        public boolean isError() {
            return !isSuccess();
        }
    
        public abstract A getResult();
    
        public abstract Exception getError();
    }
    
    public class Success<A> extends Try<A> {
    
        private final A result;
    
        public Success(A result) {
            this.result = result;
        }
    
        @Override
        public boolean isSuccess() {
            return true;
        }
    
        @Override
        public A getResult() {
            return result;
        }
    
        @Override
        public Exception getError() {
            return new UnsupportedOperationException();
        }
    
        @Override
        public boolean equals(Object that) {
            if(!(that instanceof Success)) {
                return false;
            }
            return Objects.equal(result, ((Success) that).getResult());
        }
    }
    
    public class Failure<A> extends Try<A> {
    
        private final Exception exception;
    
        public Failure(Exception exception) {
            this.exception = exception;
        }
    
        @Override
        public boolean isSuccess() {
            return false;
        }
    
        @Override
        public A getResult() {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public Exception getError() {
            return exception;
        }
    }
    

    A simple use case :

    List<Try<Integer>> result = Lists.newArrayList(1, 2, 3).stream().
        map(Try.<Integer, Integer>tryOf(i -> someMethodThrowingAnException(i))).
        collect(Collectors.toList());
    
    0 讨论(0)
提交回复
热议问题