Java 8 方法引用未处理的异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25643348/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 00:51:27  来源:igfitidea点击:

Java 8 method reference unhandled exception

javalambdajava-8

提问by Vartlok

I'm working on project with Java 8 and found one situation which I can't understand.

我正在使用 Java 8 进行项目,发现了一种我无法理解的情况。

I have code like this:

我有这样的代码:

void deleteEntity(Node node) throws SomeException {
    for (ChildNode child: node.getChildren()) {
       deleteChild(child);
    }
}

void deleteChild(Object child) throws SomeException {
    //some code
}

This code is working fine, but I can rewrite it with a method reference:

这段代码运行良好,但我可以使用方法引用重写它:

void deleteEntity(Node node) throws SomeException {
    node.getChildren().forEach(this::deleteChild);
}

And this code doesn't compile, giving the error Incompatible thrown types *SomeException* in method reference.

并且此代码无法编译,并给出错误Incompatible thrown types *SomeException* in method reference

Also IDEA gave me the error unhandled exception.

IDEA 也给了我错误unhandled exception

So, my question is why? Why code compiles with for each loop and doesn't compile with lambda?

所以,我的问题是为什么?为什么代码用 for 每个循环编译而不用 lambda 编译?

采纳答案by Jon Skeet

If you look at the Consumer<T>interface, the acceptmethod (which is what your method reference would effectively be using) isn't declared to throw any checked exceptions - therefore you can't use a method reference which isdeclared to throw a checked exception. The enhanced for loop is okay, because there you're always in a context where SomeExceptioncan be thrown.

如果你看一下Consumer<T>接口的accept方法(这是你的方法引用将有效使用什么)没有声明抛出任何checked异常-所以你不能使用它的方法引用声明为抛出checked异常。增强的 for 循环没问题,因为您总是处于SomeException可以抛出的上下文中。

You could potentially create a wrapper which converts the checked exception to an unchecked exception, and throw that. Alternatively, you could declare your own functional interface with an accept()method which doesthrow a checked exception (probably parameterizing the interface with that exception), and then write your own forEachmethod that takes that functional interface as an input.

您可能会创建一个包装器,将已检查的异常转换为未检查的异常,然后抛出该异常。或者,你可以用一个声明自己的功能接口accept(),其方法确实抛出checked异常(可能是参数化与异常接口),然后写你自己的forEach方法,它是功能接口为输入。

回答by MarcG

You may try this:

你可以试试这个:

void deleteEntity(Node node) throws SomeException {     node.getChildren().forEach(UtilException.rethrowConsumer(this::deleteChild));
    }

The UtilExceptionhelper class below lets you use any checked exceptions in Java streams. Note the stream above also throws the original checked exception thrown by this::deleteChild, and NOT some wrapping unchecked exception.

下面的UtilException帮助器类允许您在 Java 流中使用任何已检查的异常。请注意,上面的流也抛出了由 抛出的原始已检查异常this::deleteChild,而不是一些包装的未检查异常。

public final class UtilException {

@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
    void accept(T t) throws E;
    }

@FunctionalInterface
public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
    void accept(T t, U u) throws E;
    }

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

@FunctionalInterface
public interface Supplier_WithExceptions<T, E extends Exception> {
    T get() throws E;
    }

@FunctionalInterface
public interface Runnable_WithExceptions<E extends Exception> {
    void run() throws E;
    }

/** .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println)); */
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
    return t -> {
        try { consumer.accept(t); }
        catch (Exception exception) { throwAsUnchecked(exception); }
        };
    }

public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E {
    return (t, u) -> {
        try { biConsumer.accept(t, u); }
        catch (Exception exception) { throwAsUnchecked(exception); }
        };
    }

/** .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) { throwAsUnchecked(exception); return null; }
        };
    }

/** rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), */
public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) throws E {
    return () -> {
        try { return function.get(); }
        catch (Exception exception) { throwAsUnchecked(exception); return null; }
        };
    }

/** uncheck(() -> Class.forName("xxx")); */
public static void uncheck(Runnable_WithExceptions t)
    {
    try { t.run(); }
    catch (Exception exception) { throwAsUnchecked(exception); }
    }

/** uncheck(() -> Class.forName("xxx")); */
public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier)
    {
    try { return supplier.get(); }
    catch (Exception exception) { throwAsUnchecked(exception); return null; }
    }

/** uncheck(Class::forName, "xxx"); */
public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) {
    try { return function.apply(t); }
    catch (Exception exception) { throwAsUnchecked(exception); return null; }
    }

@SuppressWarnings ("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; }

}

Many other examples on how to use it (after statically importing UtilException):

关于如何使用它的许多其他示例(静态导入后UtilException):

@Test
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException {
    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .forEach(rethrowConsumer(className -> System.out.println(Class.forName(className))));

    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .forEach(rethrowConsumer(System.out::println));
    }

@Test
public void test_Function_with_checked_exceptions() throws ClassNotFoundException {
    List<Class> classes1
          = Stream.of("Object", "Integer", "String")
                  .map(rethrowFunction(className -> Class.forName("java.lang." + className)))
                  .collect(Collectors.toList());

    List<Class> classes2
          = Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
                  .map(rethrowFunction(Class::forName))
                  .collect(Collectors.toList());
    }

@Test
public void test_Supplier_with_checked_exceptions() throws ClassNotFoundException {
    Collector.of(
          rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))),
          StringJoiner::add, StringJoiner::merge, StringJoiner::toString);
    }

@Test    
public void test_uncheck_exception_thrown_by_method() {
    Class clazz1 = uncheck(() -> Class.forName("java.lang.String"));

    Class clazz2 = uncheck(Class::forName, "java.lang.String");
    }

@Test (expected = ClassNotFoundException.class)
public void test_if_correct_exception_is_still_thrown_by_method() {
    Class clazz3 = uncheck(Class::forName, "INVALID");
    }

But don't use it before understanding the following advantages, disadvantages, and limitations:

但在了解以下优点、缺点和限制之前,请勿使用它

? If the calling-code is to handle the checked exception you MUST add it to the throws clause of the method that contains the stream. The compiler will not force you to add it anymore, so it's easier to forget it.

? 如果调用代码要处理已检查的异常,则必须将其添加到包含流的方法的 throws 子句中。编译器不会再强迫您添加它,因此更容易忘记它。

? If the calling-code already handles the checked exception, the compiler WILL remind you to add the throws clause to the method declaration that contains the stream (if you don't it will say: Exception is never thrown in body of corresponding try statement).

? 如果调用代码已经处理了已检查的异常,编译器会提醒您在包含流的方法声明中添加 throws 子句(如果不这样做,它会说:在相应的 try 语句的主体中永远不会抛出异常) .

? In any case, you won't be able to surround the stream itself to catch the checked exception INSIDE the method that contains the stream (if you try, the compiler will say: Exception is never thrown in body of corresponding try statement).

? 在任何情况下,您都无法包围流本身以捕获包含流的方法内部的已检查异常(如果您尝试,编译器会说:在相应的 try 语句的主体中永远不会抛出异常)。

? If you are calling a method which literally can never throw the exception that it declares, then you should not include the throws clause. For example: new String(byteArr, "UTF-8") throws UnsupportedEncodingException, but UTF-8 is guaranteed by the Java spec to always be present. Here, the throws declaration is a nuisance and any solution to silence it with minimal boilerplate is welcome.

? 如果您正在调用一个从字面上永远不会抛出它声明的异常的方法,那么您不应该包含 throws 子句。例如: new String(byteArr, "UTF-8") 抛出 UnsupportedEncodingException,但 Java 规范保证 UTF-8 始终存在。在这里,throws 声明是一个令人讨厌的东西,欢迎任何用最少的样板来使其静音的解决方案。

? If you hate checked exceptions and feel they should never be added to the Java language to begin with (a growing number of people think this way, and I am NOT one of them), then just don't add the checked exception to the throws clause of the method that contains the stream. The checked exception will, then, behave just like an UNchecked exception.

? 如果您讨厌受检异常并且认为它们不应该从一开始就被添加到 Java 语言中(越来越多的人这样认为,而我不是其中之一),那么就不要将受检异常添加到 throws包含流的方法的子句。然后,受检异常的行为就像未受检异常一样。

? If you are implementing a strict interface where you don't have the option for adding a throws declaration, and yet throwing an exception is entirely appropriate, then wrapping an exception just to gain the privilege of throwing it results in a stacktrace with spurious exceptions which contribute no information about what actually went wrong. A good example is Runnable.run(), which does not throw any checked exceptions. In this case, you may decide not to add the checked exception to the throws clause of the method that contains the stream.

? 如果你正在实现一个严格的接口,你没有添加 throws 声明的选项,但抛出异常是完全合适的,那么包装异常只是为了获得抛出它的特权会导致堆栈跟踪与虚假异常不提供有关实际出错的信息。一个很好的例子是 Runnable.run(),它不会抛出任何已检查的异常。在这种情况下,您可能决定不向包含流的方法的 throws 子句添加已检查的异常。

? In any case, if you decide NOT to add (or forget to add) the checked exception to the throws clause of the method that contains the stream, be aware of these 2 consequences of throwing CHECKED exceptions:

? 在任何情况下,如果您决定不向包含流的方法的 throws 子句添加(或忘记添加)已检查异常,请注意抛出 CHECKED 异常的以下 2 个后果:

1) The calling-code won't be able to catch it by name (if you try, the compiler will say: Exception is never thrown in body of corresponding try statement). It will bubble and probably be catched in the main program loop by some "catch Exception" or "catch Throwable", which may be what you want anyway.

1) 调用代码将无法按名称捕获它(如果您尝试,编译器会说:在相应的 try 语句体中永远不会抛出异常)。它会冒泡,并可能在主程序循环中被某些“catch Exception”或“catch Throwable”捕获,无论如何这可能是您想要的。

2) It violates the principle of least surprise: it will no longer be enough to catch RuntimeException to be able to guarantee catching all possible exceptions. For this reason, I believe this should not be done in framework code, but only in business code that you completely control.

2)它违反了最小惊奇原则:仅仅捕获 RuntimeException 已经不足以保证捕获所有可能的异常。为此,我认为这不应该在框架代码中完成,而只能在您完全控制的业务代码中完成。

In conclusion: I believe the limitations here are not serious, and the UtilExceptionclass may be used without fear. However, it's up to you!

总结:相信这里的限制并不严重,UtilException可以放心使用类。然而,这取决于你!

回答by Vincz777

You may also declare someExceptionso that it extends RuntimeExceptioninstead of Exception. The following example code will compile:

您也可以声明someException它扩展RuntimeException而不是Exception. 以下示例代码将编译:

public class Test {

    public static void main(String[] args){
        // TODO Auto-generated method stub
        List<String> test = new ArrayList<String>();
        test.add("foo");
        test.add(null);
        test.add("bar");
        test.forEach(x -> print(x));    
    }

    public static class SomeException extends RuntimeException{
    }

    public static void print(String s) throws SomeException{
        if (s==null) throw new SomeException();
        System.out.println(s);
    }
}

The output will then be:

然后输出将是:

foo
Exception in thread "main" simpleTextLayout.Test$SomeException
at simpleTextLayout.Test.print(Test.java:22)
at simpleTextLayout.Test.lambda
list.stream().forEach(x->{
try{
System.out.println(x/0);
}catch(ArithmeticException e){
throw new RuntimeException(new MyCustomException(FirstArgument,SecondArgument));
});
(Test.java:14) at java.util.ArrayList.forEach(ArrayList.java:1249) at simpleTextLayout.Test.main(Test.java:14)

You can add a try/catchblock around the forEachstatement, however the execution of the forEachstatement will be interrupted once an exception is thrown. In the above example, the "bar"element of the list will not be printed. Also, by doing that, you will lose track of the thrown exception in your IDE.

您可以try/catchforEach语句周围添加一个块,但是forEach一旦抛出异常,语句的执行将被中断。在上面的例子中,"bar"列表的元素不会被打印出来。此外,通过这样做,您将无法跟踪 IDE 中抛出的异常。

回答by Yanish Pradhananga

**If you don't want to write your own Consumer Interface and make use of it. You can use your Custom Exception with ease as shown below. You can perform like below. **

**如果您不想编写自己的消费者接口并使用它。您可以轻松使用自定义异常,如下所示。您可以执行如下操作。**

ThrowingFunction<String, URI, URISyntaxException> toUri = URI::new;

回答by GabrielBB

Check out the library Throwing Function:

查看库Throwing Function

By applying com.pivovarit.function functional interfaces, it's possible to regain clarity and readability:

通过应用 com.pivovarit.function 功能接口,可以重新获得清晰度和可读性:

...stream()
  .map(unchecked(URI::new)) // static import of ThrowingFunction#unchecked
  .forEach(System.out::println);

and use them seamlessly with native java.util.function classes by using custom ThrowingFunction#unchecked adapters:

并通过使用自定义 ThrowingFunction#unchecked 适配器与原生 java.util.function 类无缝地使用它们:

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Stream;

public class ThrowingConsumerTest {

    public static void main(String[] args) throws IOException {

        List<Integer> myIntegerList = new ArrayList<>();
        myIntegerList.add(1);
        myIntegerList.add(2);
        myIntegerList.add(3);
        myIntegerList.add(null);
        myIntegerList.add(4);
        myIntegerList.add(5);
        myIntegerList.add(6);
        myIntegerList.add(7);
        myIntegerList.add(8);
        myIntegerList.add(9);
        myIntegerList.add(10);
        myIntegerList.add(11);
        myIntegerList.add(12);
        myIntegerList.add(13);
        myIntegerList.add(14);
        myIntegerList.add(15);
        myIntegerList.add(16);
        myIntegerList.add(17);
        myIntegerList.add(18);
        myIntegerList.add(19);
        forEach(myIntegerList.stream(), ThrowingConsumerTest::exceptionThrowingConsumerCode);
    }

    /**
     * Wrapper that converts Checked Exception to Runtime Exception
     */
    static <T, E extends Exception> Consumer<T> unchecked(ThrowingConsumer<T, E> consumer) {

        return (t) -> {
            try {
                consumer.accept(t);
            } catch (Throwable e) {
                //Lambda can return only RuntimeException.
                RuntimeException ex = new RuntimeException();
                ex.addSuppressed(e);
                throw ex;
            }
        };
    }

    /**
     * Wrapper that converts Runtime Exception to Checked Exception
     * Custom forEach; to accept the exception throwing consumer.
     */
    @SuppressWarnings("unchecked")
    static <T, E extends Exception> void forEach(Stream<T> s, ThrowingConsumer<T, E> consumer) throws E {

        try {

            s.parallel().forEach(unchecked(t -> consumer.accept(t)));
        } catch (RuntimeException e) {
            //Checked Exception can be return from here
            throw (E) e.getSuppressed()[0];
        }
    }

    /*
     * Consumer that throws Exception
     */
    @FunctionalInterface
    public interface ThrowingConsumer<T, E extends Exception> {
        void accept(T t) throws E;
    }

    static void exceptionThrowingConsumerCode(Object i) throws IOException {
        if (i == null) {
            throw new IOException();

        } else {
            System.out.println(i);
        }
    }
}

回答by Kanagavelu Sugumar

Please note that parallel stream will continue executing the elements though there is exception thrown.

请注意,尽管抛出异常,并行流将继续执行元素。

##代码##