从 Java 语言的角度来看,重新抛出已检查异常的技巧是如何工作的?
How does trick with rethrow checked exceptions works from the point of view of Java language?
我遇到过我必须在流表达式中捕获所有已检查的异常。
我看过很热门的话题:
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 {
请澄清一下。
P.S.
我将魔术发生在 throwAsUnchecked
内部进行了本地化,因为下面的代码片段是正确的
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);
}
};
}
Java 编译器理解 Class.forName()
抛出 checked 异常。这需要以某种方式得到处理。但是当 catching 那个 checked 异常并让它看起来像是被包装成一个 RuntimeException
例如 - 你解决了 编译问题。通过去掉 编译器抱怨的 "thing"。
请注意:编译器不理解调用throwAsUnchecked(exception)
将总是抛出。换句话说:通过在此处调用 method,实际上 no 方法体内的代码 must 扔。当然这不会改变运行时情况。最后,还是抛出了一个异常——令人惊讶的不是 RuntimeException,而是 ClassNotFoundException 的一个实例。
核心点是<E extends Throwable>
翻译成RuntimeException(对于编译器)。那部分解释 here!
因此,foo()
的第二个版本可以这样写:
public void foo() { ...
不再需要声明throws ClassNotFoundException!
换句话说:"tricking" 编译器认为抛出的异常是 未经检查的 异常。而这个技巧本身就植根于 generics/type-erasure.
我遇到过我必须在流表达式中捕获所有已检查的异常。 我看过很热门的话题:
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 {
请澄清一下。
P.S.
我将魔术发生在 throwAsUnchecked
内部进行了本地化,因为下面的代码片段是正确的
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);
}
};
}
Java 编译器理解 Class.forName()
抛出 checked 异常。这需要以某种方式得到处理。但是当 catching 那个 checked 异常并让它看起来像是被包装成一个 RuntimeException
例如 - 你解决了 编译问题。通过去掉 编译器抱怨的 "thing"。
请注意:编译器不理解调用throwAsUnchecked(exception)
将总是抛出。换句话说:通过在此处调用 method,实际上 no 方法体内的代码 must 扔。当然这不会改变运行时情况。最后,还是抛出了一个异常——令人惊讶的不是 RuntimeException,而是 ClassNotFoundException 的一个实例。
核心点是<E extends Throwable>
翻译成RuntimeException(对于编译器)。那部分解释 here!
因此,foo()
的第二个版本可以这样写:
public void foo() { ...
不再需要声明throws ClassNotFoundException!
换句话说:"tricking" 编译器认为抛出的异常是 未经检查的 异常。而这个技巧本身就植根于 generics/type-erasure.