【问题标题】:How does trick with rethrow checked exceptions works from the point of view of Java language?从 Java 语言的角度来看,重新抛出检查异常的技巧是如何工作的?
【发布时间】:2017-09-05 09:04:18
【问题描述】:

我遇到过我必须在流表达式中捕获所有已检查的异常。 我读过非常热门的话题:

How can I throw CHECKED exceptions from inside Java 8 streams?
并且有以下方法的答案:
我们有 3 个字符串: "java.lang.Object" "java.lang.Integer" "java.lang.Strin"

我们想按名称加载类。
因此我们需要使用Class#forName方法
Class#forName java doc)

如您所见,以下方法声明包含

throws ClassNotFoundException

因此,如果我们使用通常的循环,我们需要编写以下代码:

public void foo() throws ClassNotFoundException {
    String arr[] = new String[]{"java.lang.Object", "java.lang.Integer", "java.lang.Strin"};
    for(String className:arr){
        Class.forName(className);
    }
}

让我们尝试使用流重写它:

 public void foo() throws ClassNotFoundException {
        String arr[] = new String[]{"java.lang.Object", "java.lang.Integer", "java.lang.Strin"};
        Stream.of(arr).forEach(Class::forName);
    }

编译器说:

Error:(46, 32) java: incompatible thrown types java.lang.ClassNotFoundException in method reference

好的,让我们尝试使用上述主题的方法:

我们创建以下方法:

public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) {
    return t -> {
        try {
            consumer.accept(t);
        } catch (Exception exception) {
            throwAsUnchecked(exception);
        }
    };
}

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

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

客户端代码如下所示:

public void foo() throws ClassNotFoundException {
        String arr[] = new String[]{"java.lang.Object", "java.lang.Integer", "java.lang.Strin"};
        Stream.of(arr).forEach(rethrowConsumer(Class::forName));
    }

我想知道它是如何工作的。这对我来说真的不清楚。

让我们研究rethrowConsumer:

public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) {
    return t -> {
        try {
            consumer.accept(t);
        } catch (Exception exception) {
            throwAsUnchecked(exception);
        }
    };
}

和 throwAsUnchecked 签名看起来很像

private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {

请澄清这个烂摊子。

附言

我将魔法发生在 throwAsUnchecked 中,因为遵循 sn-p 正确

   public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) {
        return t -> {
            try {
                throw new Exception();
            } catch (Exception exception) {
                throwAsUnchecked(exception);
            }
        };
    }

但不跟随

public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) {
    return t -> {
        try {
            consumer.accept(t);
        } catch (Exception exception) {
            throw new Exception();
            //throwAsUnchecked(exception);
        }
    };
}

【问题讨论】:

  • 仅作记录:您只使用“_”表示 SOME_CONSTANT。它不会进入任何其他类型的名称。
  • @GhostCat 它是来自链接主题的复制粘贴
  • @GhostCat 实际上我不明白为什么编译器将 Throwable 解释为 RuntimeException
  • 这可以在我的回答中的链接问题中找到。具体来说,您想阅读以下内容:stackoverflow.com/a/14039011/1531124

标签: java generics exception-handling java-stream checked-exceptions


【解决方案1】:

Java 编译器知道Class.forName() 会引发checked 异常。这需要以某种方式处理。但是,当捕获已检查异常并使其看起来像是被包装到RuntimeException 中时 - 您解决了编译 问题。通过带走编译器抱怨的“东西”。

请注意:编译器确实理解调用throwAsUnchecked(exception)总是抛出。换句话说:通过在此处进行方法 调用,该方法体内实际上没有 代码必须 抛出。当然,这不会改变运行时情况。最后还是抛出了一个异常——令人惊讶的不是 RuntimeException,而是 ClassNotFoundException 的一个实例。

核心点是&lt;E extends Throwable&gt; 转换为RuntimeException(用于编译器)。这部分解释here

因此,foo() 的第二个版本可以写成:

public void foo() { ...

不再需要声明抛出 ClassNotFoundException

换句话说:这种方法通过“欺骗”编译器使其认为抛出的异常是未经检查的。而这个技巧本身就植根于泛型/类型擦除。

【讨论】:

  • 链接的解释涵盖了类型擦除的技术部分,换句话说,Java 字节码。从 Java language 的角度来看,您只有一个 unchecked 操作,这可能会导致 heap 污染,即编译之间的不匹配-time 类型和实际的运行时类型,其中包括编译时类型为RuntimeException 的已检查异常的可能性。所以实际的运行时行为并没有违反语言规范。
猜你喜欢
  • 2011-06-01
  • 1970-01-01
  • 2011-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-21
相关资源
最近更新 更多