当前位置: 首页 > 知识库问答 >
问题:

Java8方法引用未处理异常

甄文彬
2023-03-14

我正在和Java8一起做项目,发现了一个我无法理解的情况。

我有这样的代码:

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

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

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

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

而这段代码没有编译,在方法引用中给出了不兼容的抛出类型*SomeException*。

IDEA还向我提供了错误未处理的异常。

那么,我的问题是为什么?为什么代码使用for-each循环编译而不使用lambda编译?


共有3个答案

鄂育
2023-03-14

请注意,虽然引发了异常,但并行流将继续执行元素。

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);
        }
    }
}
封永嘉
2023-03-14

你可以试试这个:

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

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

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

}

关于如何使用它的许多其他示例(在静态导入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");
    }

但在了解以下优点、缺点和局限性之前,不要使用它:

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

•如果调用代码已经处理了选中的异常,编译器会提醒您将throws子句添加到包含流的方法声明中(如果您没有这样做,它会说:在相应的try语句体中从不抛出异常)。

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

•如果您调用的方法实际上永远不会抛出它声明的异常,那么您不应该包含throws子句。例如:新字符串(byteArr,“UTF-8”)抛出不支持的编码异常(UnsupportedEncodingException),但Java规范保证UTF-8始终存在。在这里,throws声明是一个讨厌的东西,任何用最少的模板使其静音的解决方案都是受欢迎的。

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

•如果您正在实现一个严格的接口,在该接口中,您没有添加throws声明的选项,但抛出异常是完全合适的,那么包装异常只是为了获得抛出它的权限,结果会导致堆栈跟踪,其中包含虚假异常,而这些异常不提供有关实际出错的信息。一个很好的例子是Runnable。run(),它不会引发任何选中的异常。在这种情况下,您可以决定不将选中的异常添加到包含流的方法的throws子句中。

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

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

2)它违反了最小意外原则:捕获RuntimeException将不再足以保证捕获所有可能的异常。出于这个原因,我认为这不应该在框架代码中完成,而只应该在您完全控制的业务代码中完成。

总之:我认为这里的局限性并不严重,可以毫无顾虑地使用UtilException类。然而,这取决于你!

  • 参考文献:
孔君浩
2023-03-14

如果您查看消费者

您可以潜在地创建一个包装器,将选中的异常转换为未选中的异常,并抛出它。或者,您可以使用接受()方法声明自己的函数接口,该方法确实会抛出选中的异常(可能会将接口与该异常参数化),然后编写您自己的foreach方法,将该函数接口作为输入。

 类似资料:
  • 主要内容:1 Java8 方法引用的介绍,2 Java8 方法引用的分类,3 Java8 方法引用:引用静态方法,4 Java8 方法引用:引用实例方法,5 Java8 方法引用:引用构造方法1 Java8 方法引用的介绍 Java提供了一个新功能,称为Java 8中的方法引用。方法引用用于引用功能接口的方法。它是lambda表达式的紧凑和简单形式。每次使用lambda表达式仅引用方法时,都可以将lambda表达式替换为方法引用。在本教程中,我们将详细解释方法参考概念。 2 Java8 方法引用

  • 我有一个抛出检查异常的方法: 我正在尝试创建一个通用包装器,它将优雅地处理异常。 现在我正在使用,它给我编译时错误。我可能错过了什么?

  • 问题内容: 我正在使用Java 8进行项目开发,发现了一种我无法理解的情况。 我有这样的代码: 这段代码可以正常工作,但是我可以使用方法参考将其重写: 而且此代码无法编译,从而导致错误。 IDEA也给了我错误。 所以,我的问题是为什么?为什么代码在每个循环中都用编译,而lambda不编译? 问题答案: 如果您查看接口,则该方法(您的方法引用将有效使用的方法)未声明为抛出任何已检查的异常- 因此,您

  • [错误:flatter/lib/ui/ui_dart_state.cc(157)]未处理的异常:NoSuchMethodError:对null调用了方法“validate”。E/flatter(6538):接收器:null E/flatter(6538):尝试调用:validate()E/flatter(6538):#0对象。无此方法(dart:core patch/object_patch.da

  • 引入异常处理 尽管只有寥寥几行代码,我们却已经实现了可工作的半协程调度器(缺失异常处理)。 没关系,下面先rollback回return的实现,开始引入异常处理,目标是在嵌套生成器之间正确向上抛出异常,跨生成器捕获异常。 <?php // 为Gen引入throw方法 class Gen { // PHP7 之前 关键词不能用作名字 public function throw_(\

  • 问题内容: 它在DEBUG = True模式下运行。有时,当遇到错误时,它可能会抛出带有追溯信息的错误消息,但有时,它仅显示以下行: 我必须切换到开发服务器才能查看详细消息。 遇到错误时,如何使它始终显示回溯消息? 问题答案: 也许你可以使用此代码段,这会将异常记录在apache的日志中: 把它放在你的settings.py: 在你的代码中: