猿问

使用重新抛出异常的异常处理程序有什么明显的区别

给定一个可能抛出的函数:


public static int f() throws Exception {

    // do something

}

这段代码有什么办法:


public static int catchF() throws Exception {

    try {

        return f();

    } catch (Exception ex) {

        throw ex;

    }

}

和直接打电话有什么不同吗f?即调用者可以通过检查异常来检测差异吗?catchF使用而不是有任何明显的开销吗f?


如果没有区别,编译器或 JVM 能否将对的调用优化catchF为对的直接调用f?


虽然这看起来像是一件奇怪的事情,但用例是在先前隐藏异常之后在类型级别重新引入异常:

class Test {


    // Hide the exception.

    public static <X extends Exception, T> T throwUnchecked(Exception ex) throws X {

        throw (X) ex;

    }


    // Interface for functions which throw.

    interface Throws<T, R, X extends Exception> {

        R apply(T t) throws X;

    }



    // Convert a function which throws a visible exception into one that throws a hidden exception.

    public static <T, R, X extends Exception> Function<T, R> wrap(Throws<T, R, X> thrower) {

        return t -> {

            try {

                return thrower.apply(t);

            } catch(Exception ex) {

                return throwUnchecked(ex);

            }

        };

    }


    // Unhide an exception.

    public static <R, X extends Exception> R unwrap(Supplier<R> supp) throws X {

        try {

            return supp.get();

        } catch (Exception ex) {

            throw (X)ex;

        }

    }


    public static Stream<Integer> test(Stream<String> ss) throws NumberFormatException {

        return Test.<Stream<Integer>, NumberFormatException>unwrap(

                () -> ss.map(wrap(Integer::parseInt))

        );

    }


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

        final List<Integer> li = test(Arrays.stream(new String[]{"1", "2", "3"})).collect(toList());

        System.out.println(li);

    }

}

目的是将抛出异常的函数包装到在类型级别隐藏异常的函数中。这使得异常可用于例如流。


守着星空守着你
浏览 128回答 2
2回答

皈依舞

与直接调用 f 有什么不同吗?不。即调用者可以通过检查异常来检测差异吗?不,因为此时您没有构造新的异常。堆栈跟踪是在调用的位置构建的new WhateverException(...)(不是在调用位置的位置throw,尽管它们通常位于同一位置)。通常,如果由于异常而需要进行一些清理,则可以重新抛出捕获的异常:try {  // ...} catch (SomeException e) {  // Clean up resources.  throw e;}调用堆栈展开时发生的事情对调用者来说既不可见也不相关。快速演示可以显示堆栈跟踪是相同的,无论异常是被捕获并重新抛出还是仅仅允许传播。使用 catchF 而不是 f 是否有任何明显的开销?构造异常的开销将远远超过此冗余构造的任何开销。

ibeautiful

简单地重新抛出并且不影响异常的已知类型的异常处理程序绝对没有语义效果。那么,理论上,编译器或 JVM 可以优化 try-catch out。在实践中,我怀疑他们这样做,因为这样的代码应该很少见并且不在热路径上(永远不应该有例外);实现这样的优化可能不值得付出努力。
随时随地看视频慕课网APP

相关分类

Java
我要回答