【问题标题】:Java 8 Lambda function that throws exception?抛出异常的Java 8 Lambda函数?
【发布时间】:2018-10-17 00:33:12
【问题描述】:

我知道如何创建对具有String 参数并返回int 的方法的引用,它是:

Function<String, Integer>

但是,如果函数抛出异常,这不起作用,比如定义为:

Integer myMethod(String s) throws IOException

我将如何定义这个引用?

【问题讨论】:

标签: java lambda java-8


【解决方案1】:

如果你有 lombok,你可以用 @SneakyThrows 注释你的方法

SneakyThrow 不会默默吞下、包装到 RuntimeException 或以其他方式修改列出的已检查异常类型的任何异常。 JVM 不检查受检异常系统的一致性; javac 可以,这个注解可以让你选择退出它的机制。

https://projectlombok.org/features/SneakyThrows

【讨论】:

  • 太棒了。我爱它。它允许您在不使用 throws 声明的情况下抛出已检查的异常。当您将函数传递给 Lambda 时非常有用。 baeldung.com/java-sneaky-throws
【解决方案2】:

我使用一个名为unchecked() 的重载实用函数来处理多个用例。


一些示例用法

unchecked(() -> new File("hello.txt").createNewFile());

boolean fileWasCreated = unchecked(() -> new File("hello.txt").createNewFile());

myFiles.forEach(unchecked(file -> new File(file.path).createNewFile()));

支持实用程序

public class UncheckedUtils {

    @FunctionalInterface
    public interface ThrowingConsumer<T> {
        void accept(T t) throws Exception;
    }

    @FunctionalInterface
    public interface ThrowingSupplier<T> {
        T get() throws Exception;
    }

    @FunctionalInterface
    public interface ThrowingRunnable {
        void run() throws Exception;
    }

    public static <T> Consumer<T> unchecked(
            ThrowingConsumer<T> throwingConsumer
    ) {
        return i -> {
            try {
                throwingConsumer.accept(i);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        };
    }

    public static <T> T unchecked(
            ThrowingSupplier<T> throwingSupplier
    ) {
        try {
            return throwingSupplier.get();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void unchecked(
            ThrowingRunnable throwing
    ) {
        try {
            throwing.run();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}

【讨论】:

    【解决方案3】:

    创建一个自定义返回类型,该类型将传播检查的异常。这是创建一个镜像现有功能接口的新接口的替代方法,只需稍微修改功能接口的方法上的“抛出异常”。

    定义

    CheckedValueSupplier

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

    检查值

    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);
            }
        }
    
    }
    

    用法

    //  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();
    

    发生了什么事?

    创建一个引发检查异常的单一功能接口 (CheckedValueSupplier)。这将是唯一允许检查异常的功能接口。所有其他功能接口都将利用CheckedValueSupplier 包装任何引发检查异常的代码。

    CheckedValue 类将保存执行任何引发检查异常的逻辑的结果。这可以防止已检查异常的传播,直到代码尝试访问 CheckedValue 的实例所包含的值。

    这种方法的问题。

    • 我们现在抛出“异常”,有效地隐藏了最初抛出的特定类型。
    • 在调用 CheckedValue#get() 之前,我们不知道发生了异常。

    消费者等

    某些功能接口(例如Consumer)必须以不同的方式处理,因为它们不提供返回值。

    代替消费者的功能

    一种方法是使用函数而不是消费者,这适用于处理流。

        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
    

    升级

    或者,您可以随时升级到RuntimeException。还有其他答案涵盖了从 Consumer 中升级检查的异常。

    不要食用。

    完全避免使用函数式接口并使用老式的 for 循环。

    【讨论】:

      【解决方案4】:

      您需要执行以下操作之一。

      • 如果是你的代码,那么定义你自己的函数接口来声明被检查的异常:

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

        并使用它:

        void foo (CheckedFunction f) { ... }
        
      • 否则,将Integer myMethod(String s) 包装在不声明检查异常的方法中:

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

        然后:

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

        或:

        Function<String, Integer> f =
            (String t) -> {
                try {
                   return myMethod(t);
                }
                catch(IOException e) {
                    throw new UncheckedIOException(e);
                }
            };
        

      【讨论】:

      • 如果您使用默认方法,您实际上可以扩展 ConsumerFunction -- 请参阅下面的答案。
      • 我认为这可以通过one-liner来完成。
      • 小优化:也可以使用方法引用this::myWrappedMethod代替(String t) -&gt; myWrappedMethod(t)
      • 一个更通用的方法是定义这样的检查函数 @FunctionalInterface public interface CheckedFunction { R apply(T t) throws E;通过这种方式,您还可以定义函数抛出的异常,并且可以将接口重用于任何代码。
      • 从 C# 转到 Java 感觉就像全身心投入。
      【解决方案5】:

      我是一个小型库的作者,它具有一些通用魔法,可以在任何地方抛出任何 Java 异常而不需要捕获它们或将它们包装到 RuntimeException

      用法: unchecked(() -&gt; methodThrowingCheckedException())

      public class UncheckedExceptions {
      
          /**
           * throws {@code exception} as unchecked exception, without wrapping exception.
           *
           * @return will never return anything, return type is set to {@code exception} only to be able to write <code>throw unchecked(exception)</code>
           * @throws T {@code exception} as unchecked exception
           */
          @SuppressWarnings("unchecked")
          public static <T extends Throwable> T unchecked(Exception exception) throws T {
              throw (T) exception;
          }
      
      
          @FunctionalInterface
          public interface UncheckedFunction<R> {
              R call() throws Exception;
          }
      
          /**
           * Executes given function,
           * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
           *
           * @return result of function
           * @see #unchecked(Exception)
           */
          public static <R> R unchecked(UncheckedFunction<R> function) {
              try {
                  return function.call();
              } catch (Exception e) {
                  throw unchecked(e);
              }
          }
      
      
          @FunctionalInterface
          public interface UncheckedMethod {
              void call() throws Exception;
          }
      
          /**
           * Executes given method,
           * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
           *
           * @see #unchecked(Exception)
           */
          public static void unchecked(UncheckedMethod method) {
              try {
                  method.call();
              } catch (Exception e) {
                  throw unchecked(e);
              }
          }
      }
      

      来源:https://github.com/qoomon/unchecked-exceptions-java

      【讨论】:

        【解决方案6】:

        Sneaky throw idiom 允许绕过 Lambda 表达式的CheckedException。将 CheckedException 包裹在 RuntimeException 中并不适合严格的错误处理。

        它可以用作Java集合中使用的Consumer函数。

        这是jib's answer的简单改进版。

        import static Throwing.rethrow;
        
        @Test
        public void testRethrow() {
            thrown.expect(IOException.class);
            thrown.expectMessage("i=3");
        
            Arrays.asList(1, 2, 3).forEach(rethrow(e -> {
                int i = e.intValue();
                if (i == 3) {
                    throw new IOException("i=" + i);
                }
            }));
        }
        

        这只是将 lambda 包装在 rethrow 中。它使 CheckedException 重新抛出任何在您的 lambda 中抛出的 Exception

        public final class Throwing {
            private Throwing() {}
        
            @Nonnull
            public static <T> Consumer<T> rethrow(@Nonnull final ThrowingConsumer<T> consumer) {
                return consumer;
            }
        
            /**
             * The compiler sees the signature with the throws T inferred to a RuntimeException type, so it
             * allows the unchecked exception to propagate.
             * 
             * http://www.baeldung.com/java-sneaky-throws
             */
            @SuppressWarnings("unchecked")
            @Nonnull
            public static <E extends Throwable> void sneakyThrow(@Nonnull Throwable ex) throws E {
                throw (E) ex;
            }
        
        }
        

        找到完整的代码和单元测试here

        【讨论】:

          【解决方案7】:

          使用Jool Library 或从JOOQjOOλ library。它不仅提供了未经检查的异常处理接口,还为 Seq 类提供了许多有用的方法。

          此外,它还包含多达 16 个参数的功能接口。此外,它还提供了用于不同场景的 Tuple 类。

          Jool Git Link

          特别是在库查找org.jooq.lambda.fi.util.function 包。它包含来自 Java-8 的所有接口,前面带有 Checked。参考如下:-

          【讨论】:

          • 这是一篇博客文章,展示了 jOOλ 中的工作原理示例:blog.jooq.org/…
          【解决方案8】:

          您实际上可以使用 Java 8 的默认方法,使用处理异常的新接口扩展 Consumer(和 Function 等)!

          考虑这个接口(扩展Consumer):

          @FunctionalInterface
          public interface ThrowingConsumer<T> extends Consumer<T> {
          
              @Override
              default void accept(final T elem) {
                  try {
                      acceptThrows(elem);
                  } catch (final Exception e) {
                      // Implement your own exception handling logic here..
                      // For example:
                      System.out.println("handling an exception...");
                      // Or ...
                      throw new RuntimeException(e);
                  }
              }
          
              void acceptThrows(T elem) throws Exception;
          
          }
          

          然后,例如,如果您有一个列表:

          final List<String> list = Arrays.asList("A", "B", "C");
          

          如果您想通过一些引发异常的代码来使用它(例如使用forEach),您通常会设置一个 try/catch 块:

          final Consumer<String> consumer = aps -> {
              try {
                  // maybe some other code here...
                  throw new Exception("asdas");
              } catch (final Exception ex) {
                  System.out.println("handling an exception...");
              }
          };
          list.forEach(consumer);
          

          但是有了这个新接口,你可以用 lambda 表达式实例化它,编译器不会报错:

          final ThrowingConsumer<String> throwingConsumer = aps -> {
              // maybe some other code here...
              throw new Exception("asdas");
          };
          list.forEach(throwingConsumer);
          

          或者甚至只是让它更简洁!:

          list.forEach((ThrowingConsumer<String>) aps -> {
              // maybe some other code here...
              throw new Exception("asda");
          });
          

          更新:看起来Durian 中有一个非常不错的实用程序库部分,称为Errors,可用于以更大的灵活性解决此问题。例如,在我上面的实现中,我明确定义了错误处理策略(System.out...throw RuntimeException),而 Durian 的错误允许您通过大量实用程序方法动态应用策略。感谢sharing it,@NedTwigg!。

          示例用法:

          list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c)));
          

          【讨论】:

          • 因此您有一组接口(Function、Consumer、Supplier...)和一组处理错误的策略(Throwing、System.out.println...)。我认为there's a way 可以轻松使用具有任何功能的任何策略,而无需复制粘贴“ThrowingConsumer、ThrowingFunction 等”。
          • 一段时间后...我决定使用未经检查的异常,而不使用任何额外的功能接口或新库 -> 简单的道路,更少的输入,更快的交付,不是吗。
          • 这里是an improved version 使用偷偷摸摸的投掷成语。无需将 RuntimeException 解包到 CheckException 中。
          【解决方案9】:

          我会做一些通用的事情:

          public interface Lambda {
          
              @FunctionalInterface
              public interface CheckedFunction<T> {
          
                  T get() throws Exception;
              }
          
              public static <T> T handle(CheckedFunction<T> supplier) {
                  try {
                      return supplier.get();
                  } catch (Exception exception) {
                      throw new RuntimeException(exception);
          
                  }
              }
          }
          

          用法:

           Lambda.handle(() -> method());
          

          【讨论】:

            【解决方案10】:

            提供的几个解决方案使用 E 的通用参数来传递抛出异常的类型。

            更进一步,而不是传入异常类型,而是传入异常类型的消费者,如...

            Consumer<E extends Exception>
            

            您可以创建几个可重用的Consumer&lt;Exception&gt; 变体,它们将满足您应用程序的常见异常处理需求。

            【讨论】:

              【解决方案11】:

              如果您不介意使用第 3 方库 (Vavr),您可以编写

              CheckedFunction1<String, Integer> f = this::myMethod;
              

              它还有所谓的 Try monad 来处理错误:

              Try(() -> f.apply("test")) // results in a Success(Integer) or Failure(Throwable)
                      .map(i -> ...) // only executed on Success
                      ...
              

              请阅读更多here

              免责声明:我是 Vavr 的创造者。

              【讨论】:

                【解决方案12】:

                默认情况下,Java 8 Function 不允许抛出异常,正如多个答案中所建议的,有很多方法可以实现它,一种方法是:

                @FunctionalInterface
                public interface FunctionWithException<T, R, E extends Exception> {
                    R apply(T t) throws E;
                }
                

                定义为:

                private FunctionWithException<String, Integer, IOException> myMethod = (str) -> {
                    if ("abc".equals(str)) {
                        throw new IOException();
                    }
                  return 1;
                };
                

                并在调用者方法中添加throwstry/catch 相同的异常。

                【讨论】:

                  【解决方案13】:

                  您可以使用unthrow wrapper

                  Function<String, Integer> func1 = s -> Unthrow.wrap(() -> myMethod(s));
                  

                  Function<String, Integer> func2 = s1 -> Unthrow.wrap((s2) -> myMethod(s2), s1);
                  

                  【讨论】:

                    【解决方案14】:

                    如果你不介意使用第三方库,用cyclops-react,我贡献的一个库,你可以使用FluentFunctions API 来写

                     Function<String, Integer> standardFn = FluentFunctions.ofChecked(this::myMethod);
                    

                    ofChecked 采用 jOOλ CheckedFunction 并将软化的引用返回到标准(未检查)JDK java.util.function.Function。

                    或者,您可以通过 FluentFunctions api 继续使用捕获的函数!

                    例如要执行你的方法,最多重试5次并记录它的状态,你可以写

                      FluentFunctions.ofChecked(this::myMethod)
                                     .log(s->log.debug(s),e->log.error(e,e.getMessage())
                                     .try(5,1000)
                                     .apply("my param");
                    

                    【讨论】:

                      【解决方案15】:

                      这里已经发布了很多很棒的回复。只是试图从不同的角度解决问题。这只是我的 2 美分,如果我在某个地方错了,请纠正我。

                      FunctionalInterface 中的Throws 子句不是一个好主意

                      我认为强制 throws IOException 可能不是一个好主意,原因如下

                      • 在我看来,这就像 Stream/Lambda 的反模式。整个想法是调用者将决定提供什么代码以及如何处理异常。在许多情况下,IOException 可能不适用于客户端。例如,如果客户端从缓存/内存中获取价值,而不是执行实际的 I/O。

                      • 此外,流中的异常处理变得非常可怕。例如,如果我使用您的 API,我的代码将如下所示

                                   acceptMyMethod(s -> {
                                        try {
                                            Integer i = doSomeOperation(s);
                                            return i;
                                        } catch (IOException e) {
                                            // try catch block because of throws clause
                                            // in functional method, even though doSomeOperation
                                            // might not be throwing any exception at all.
                                            e.printStackTrace();
                                        }
                                        return null;
                                    });
                        

                        是不是很丑?此外,正如我在第一点中提到的,doSomeOperation 方法可能会或可能不会抛出 IOException(取决于客户端/调用者的实现),但由于您的 FunctionalInterface 方法中的 throws 子句,我总是必须编写尝试捕捉。

                      如果我真的知道这个 API 会抛出 IOException,我该怎么办

                      • 那么我们可能将 FunctionalInterface 与典型的接口混淆了。如果你知道这个 API 会抛出 IOException,那么你很可能也知道一些默认/抽象行为。我认为您应该定义一个接口并按如下方式部署您的库(使用默认/抽象实现)

                        public interface MyAmazingAPI {
                            Integer myMethod(String s) throws IOException;
                        }
                        

                        但是,客户端的 try-catch 问题仍然存在。如果我在流中使用你的 API,我仍然需要在可怕的 try-catch 块中处理 IOException。

                      • 提供一个默认的流友好API,如下所示

                        public interface MyAmazingAPI {
                            Integer myMethod(String s) throws IOException;
                        
                            default Optional<Integer> myMethod(String s, Consumer<? super Exception> exceptionConsumer) {
                                try {
                                    return Optional.ofNullable(this.myMethod(s));
                                } catch (Exception e) {
                                    if (exceptionConsumer != null) {
                                        exceptionConsumer.accept(e);
                                    } else {
                                        e.printStackTrace();
                                    }
                                }
                        
                                return Optional.empty();
                            }
                        }
                        

                        默认方法以消费者对象为参数,负责处理异常。现在,从客户端的角度来看,代码将如下所示

                        strStream.map(str -> amazingAPIs.myMethod(str, Exception::printStackTrace))
                                        .filter(Optional::isPresent)
                                        .map(Optional::get).collect(toList());
                        

                        不错吧?当然,可以使用记录器或其他处理逻辑来代替 Exception::printStackTrace。

                      • 您还可以公开类似于https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function- 的方法。这意味着您可以公开另一个方法,该方法将包含先前方法调用的异常。缺点是你现在让你的 API 有状态,这意味着你需要处理线程安全,这最终会成为性能问题。只是一个可以考虑的选择。

                      【讨论】:

                      • 我同意将已检查的异常转换为未检查的异常或吞下异常不是一个好主意,因为无法知道Stream 的哪个元素引发了异常。因此,我喜欢拥有一个异常处理程序并过滤无效结果的想法。请注意,您的 MyAmazingAPI 实际上是 FunctionalInterface(因此您可以添加 @FunctionalInterface 注释)。你也可以有一个默认值而不是使用Optional.empty()
                      【解决方案16】:

                      我认为Durian's Errors class 结合了上述各种建议的许多优点。

                      要将 Durian 包含在您的项目中,您可以:

                      【讨论】:

                      • 或者你可以只使用RxJava,因为流需要固有的错误处理,如果你的管道中有东西抛出异常,那么它很可能是一个可观察的流。这也不会强制库的下游消费者使用 Java 8。
                      • 请注意,Durian 自 2016 年 6 月以来没有任何新版本。这不是一个噱头,但需要牢记。
                      • 榴莲维护者在这里。什么坏了?如果用户发现错误或重要的缺失功能,我们会迅速发布错误修复。该库很简单,因此我们没有任何错误报告,因此我们不需要发布任何错误修复。
                      • 请记住,目前,您需要复制的不仅仅是“两个小类”。
                      【解决方案17】:

                      您可以为此使用ET。 ET 是一个用于异常转换/翻译的小型 Java 8 库。

                      使用 ET 看起来像这样:

                      // Do this once
                      ExceptionTranslator et = ET.newConfiguration().done();
                      
                      ...
                      
                      // if your method returns something
                      Function<String, Integer> f = (t) -> et.withReturningTranslation(() -> myMethod(t));
                      
                      // if your method returns nothing
                      Consumer<String> c = (t) -> et.withTranslation(() -> myMethod(t));
                      

                      ExceptionTranslator 实例是线程安全的,可以被多个组件共享。您可以根据需要配置更具体的异常转换规则(例如FooCheckedException -&gt; BarRuntimeException)。 如果没有其他规则可用,检查的异常会自动转换为RuntimeException

                      (免责声明:我是 ET 的作者)

                      【讨论】:

                      • 看起来你是这个库的作者。根据SO rules,您必须在您的回答中披露您的隶属关系。请明确添加您编写此库的答案(其他与 ET 相关的答案相同)。
                      • 您好 Tagir,感谢您的提示。我更新了答案。
                      【解决方案18】:

                      你可以。

                      扩展 @marcg 的 UtilException 并在必要时添加通用 &lt;E extends Exception&gt;:这样,编译器将再次强制您添加 throw 子句,一切就好像您可以在 java 8 的流上本地抛出已检查异常一样。

                      public final class LambdaExceptionUtil {
                      
                          @FunctionalInterface
                          public interface Function_WithExceptions<T, R, E extends Exception> {
                              R apply(T t) throws E;
                          }
                      
                          /**
                           * .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName))
                           */
                          public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E  {
                              return t -> {
                                  try {
                                      return function.apply(t);
                                  } catch (Exception exception) {
                                      throwActualException(exception);
                                      return null;
                                  }
                              };
                          }
                      
                          @SuppressWarnings("unchecked")
                          private static <E extends Exception> void throwActualException(Exception exception) throws E {
                              throw (E) exception;
                          }
                      
                      }
                      
                      public class LambdaExceptionUtilTest {
                      
                          @Test
                          public void testFunction() throws MyTestException {
                              List<Integer> sizes = Stream.of("ciao", "hello").<Integer>map(rethrowFunction(s -> transform(s))).collect(toList());
                              assertEquals(2, sizes.size());
                              assertEquals(4, sizes.get(0).intValue());
                              assertEquals(5, sizes.get(1).intValue());
                          }
                      
                          private Integer transform(String value) throws MyTestException {
                              if(value==null) {
                                  throw new MyTestException();
                              }
                              return value.length();
                          }
                      
                          private static class MyTestException extends Exception { }
                      }
                      

                      【讨论】:

                        【解决方案19】:

                        但是,您可以创建自己的 FunctionalInterface,如下所示..

                        @FunctionalInterface
                        public interface UseInstance<T, X extends Throwable> {
                          void accept(T instance) throws X;
                        }
                        

                        然后使用 Lambda 或引用实现它,如下所示。

                        import java.io.FileWriter;
                        import java.io.IOException;
                        
                        //lambda expressions and the execute around method (EAM) pattern to
                        //manage resources
                        
                        public class FileWriterEAM  {
                          private final FileWriter writer;
                        
                          private FileWriterEAM(final String fileName) throws IOException {
                            writer = new FileWriter(fileName);
                          }
                          private void close() throws IOException {
                            System.out.println("close called automatically...");
                            writer.close();
                          }
                          public void writeStuff(final String message) throws IOException {
                            writer.write(message);
                          }
                          //...
                        
                          public static void use(final String fileName, final UseInstance<FileWriterEAM, IOException> block) throws IOException {
                        
                            final FileWriterEAM writerEAM = new FileWriterEAM(fileName);    
                            try {
                              block.accept(writerEAM);
                            } finally {
                              writerEAM.close();
                            }
                          }
                        
                          public static void main(final String[] args) throws IOException {
                        
                            FileWriterEAM.use("eam.txt", writerEAM -> writerEAM.writeStuff("sweet"));
                        
                            FileWriterEAM.use("eam2.txt", writerEAM -> {
                                writerEAM.writeStuff("how");
                                writerEAM.writeStuff("sweet");      
                              });
                        
                            FileWriterEAM.use("eam3.txt", FileWriterEAM::writeIt);     
                        
                          }
                        
                        
                         void writeIt() throws IOException{
                             this.writeStuff("How ");
                             this.writeStuff("sweet ");
                             this.writeStuff("it is");
                        
                         }
                        
                        }
                        

                        【讨论】:

                          【解决方案20】:
                          public void frankTest() {
                              int pageId= -1;
                          
                              List<Book> users= null;
                              try {
                                  //Does Not Compile:  Object page=DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> new Portal(rw.getInt("id"), "", users.parallelStream().filter(uu -> uu.getVbid() == rw.getString("user_id")).findFirst().get(), rw.getString("name")));
                          
                                  //Compiles:
                                  Object page= DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> { 
                                      try {
                                          final Book bk= users.stream().filter(bp -> { 
                                              String name= null;
                                              try {
                                                  name = rw.getString("name");
                                              } catch (Exception e) {
                                                  // TODO Auto-generated catch block
                                                  e.printStackTrace();
                                              }
                                              return bp.getTitle().equals(name); 
                                          }).limit(1).collect(Collectors.toList()).get(0);
                                      } catch (Exception e) {
                                          // TODO Auto-generated catch block
                                          e.printStackTrace();
                                      }
                                      return new Portal(rw.getInt("id"), "", users.get(0), rw.getString("name")); 
                                  } );
                              } catch (Exception e) {
                                  // TODO Auto-generated catch block
                                  e.printStackTrace();
                              }
                          }
                          

                          【讨论】:

                          • 介意评论你的作品吗?纯代码答案没那么有用。
                          • @Franky,您可以通过使用 4 个间距来修复您的演示文稿,而不是 &lt;code&gt;/&lt;code&gt; :)
                          【解决方案21】:

                          我在 lambda 中遇到了 Class.forName 和 Class.newInstance 这个问题,所以我就这样做了:

                          public Object uncheckedNewInstanceForName (String name) {
                          
                              try {
                                  return Class.forName(name).newInstance();
                              }
                              catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                                  throw new RuntimeException(e);
                              }
                          }
                          

                          在 lambda 内部,我没有调用 Class.forName("myClass").newInstance(),而是调用了 uncheckedNewInstanceForName ("myClass")

                          【讨论】:

                            【解决方案22】:

                            这个问题也一直困扰着我;这就是我创建this project 的原因。

                            有了它,你可以做到:

                            final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;
                            

                            JDK 定义了总共 39 个接口,它们具有这样的 Throwing 等价物;这些都是在流中使用的@FunctionalInterfaces(基础Stream,还有IntStreamLongStreamDoubleStream)。

                            随着它们每个都扩展了它们的非抛出对应物,您也可以直接在 lambdas 中使用它们:

                            myStringStream.map(f) // <-- works
                            

                            默认行为是,当您抛出的 lambda 抛出已检查异常时,会抛出 ThrownByLambdaException 并以已检查异常为原因。因此,您可以捕捉到这一点并找出原因。

                            其他功能也可用。

                            【讨论】:

                            • 我真的很喜欢这个想法,我只希望你按照这里的建议将 throwables 设为通用:javaspecialists.eu/archive/Issue221.html,例如:@FunctionalInterface public interface SupplierWithCE&lt;T, X extends Exception&gt; { T get() throws X; } - 这样用户就不需要捕捉Throwable,而是特定的检查异常。
                            • @Zoltán 每次都声明异常会很痛苦;此外,您始终可以只使用 .apply() 而不是 .doApply() 并捕获 ThrownByLambdaException,您将有原始异常作为原因(或者您可以使用 rethrow(...).as(MyRuntimeException.class)
                            • 我认为有(某种)a way around this
                            • @NedTwigg 我早就解决了这个问题;我现在可以使用Throwing.runnable() 和其他人,始终具有链接功能
                            • 链接功能非常酷!我的评论是关于 ThrowingRunnable 是否应该具有通用异常。 Zoltan 询问您的库是否可以将参数作为通用参数,您说使用起来会很痛苦。我的链接是一些代码行,这些代码行显示了一种使异常通用的方法,而不是痛苦。除非我看错了,否则您的库中的异常不是通用的(这是一个合理的设计选择,因为将它们设为通用并没有太多实用性)。
                            【解决方案23】:

                            我正在做的是允许用户在出现异常时给出他真正想要的值。 所以我有一些看起来像这样的东西

                            public static <T, R> Function<? super T, ? extends R> defaultIfThrows(FunctionThatThrows<? super T, ? extends R> delegate, R defaultValue) {
                                return x -> {
                                    try {
                                        return delegate.apply(x);
                                    } catch (Throwable throwable) {
                                        return defaultValue;
                                    }
                                };
                            }
                            
                            @FunctionalInterface
                            public interface FunctionThatThrows<T, R> {
                                R apply(T t) throws Throwable;
                            }
                            

                            然后可以这样调用:

                            defaultIfThrows(child -> child.getID(), null)
                            

                            【讨论】:

                            • This 是这个想法的扩展,它区分了“默认值”策略(如您的答案)和“重新抛出 RuntimeException”策略,其中默认值不是必要的。
                            【解决方案24】:

                            使用函数包装器的另一种解决方案是返回结果包装器的一个实例,例如 Success,如果一切顺利,则返回一个实例,例如 Failure。

                            一些代码来澄清事情:

                            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;
                                }
                            }
                            

                            一个简单的用例:

                            List<Try<Integer>> result = Lists.newArrayList(1, 2, 3).stream().
                                map(Try.<Integer, Integer>tryOf(i -> someMethodThrowingAnException(i))).
                                collect(Collectors.toList());
                            

                            【讨论】:

                              【解决方案25】:

                              这不是 Java 8 特有的。您正在尝试编译与以下内容等效的内容:

                              interface I {
                                  void m();
                              }
                              class C implements I {
                                  public void m() throws Exception {} //can't compile
                              }
                              

                              【讨论】:

                              • 问题是“我将如何定义这个引用?”。这实际上并不能回答问题。它只是澄清了问题所在。
                              【解决方案26】:

                              免责声明:我还没有使用过 Java 8,只是阅读了一下。

                              Function&lt;String, Integer&gt; 不会抛出IOException,所以你不能在其中放入任何throws IOException 的代码。如果您正在调用一个需要 Function&lt;String, Integer&gt; 的方法,那么您传递给该方法的 lambda 不能抛出 IOException,句点。你可以像这样写一个 lambda(我认为这是 lambda 语法,不确定):

                              (String s) -> {
                                  try {
                                      return myMethod(s);
                                  } catch (IOException ex) {
                                      throw new RuntimeException(ex);
                                      // (Or do something else with it...)
                                  }
                              }
                              

                              或者,如果您将 lambda 传递给的方法是您自己编写的,您可以定义一个新的函数式接口并将其用作参数类型,而不是 Function&lt;String, Integer&gt;

                              public interface FunctionThatThrowsIOException<I, O> {
                                  O apply(I input) throws IOException;
                              }
                              

                              【讨论】:

                              • 在你的接口前添加@FunctionalInterface注解,只有这样它才能用于lambdas。
                              • @Gangnus:@FunctionalInterface 注释不是必需的,它可用于 lambda。建议进行完整性检查。
                              猜你喜欢
                              • 2019-03-04
                              • 2017-06-14
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2021-08-31
                              • 2012-07-23
                              相关资源
                              最近更新 更多