如何包装已检查的异常但将原始运行时异常保留在 Java 中

How to wrap checked exceptions but keep the original runtime exceptions in Java

我有一些代码可能会抛出检查异常和运行时异常。

我想捕获已检查的异常并将其包装为运行时异常。但是如果抛出 RuntimeException,我不必包装它,因为它已经是一个运行时异常。

我的解决方案有点开销,不是 "neat":

try {
  // some code that can throw both checked and runtime exception
} catch (RuntimeException e) {
  throw e;
} catch (Exception e) {
  throw new RuntimeException(e);
}

有没有更优雅的方法?

不是真的。

如果你经常这样做,你可以把它藏在辅助方法中。

static RuntimeException unchecked(Throwable t){
    if (t instanceof RuntimeException){
      return (RuntimeException) t;
    } else if (t instanceof Error) { // if you don't want to wrap those
      throw (Error) t;
    } else {
      return new RuntimeException(t);
    }
}

try{
 // ..
}
catch (Exception e){
   throw unchecked(e);
}

我使用 "blind" 重新抛出来传递已检查的异常。我已经使用它来通过 Streams API ,在那里我不能使用抛出检查异常的 lambda。例如,我们有 ThrowingXxxxx 功能接口,因此可以传递已检查的异常。

这让我可以自然地在调用者中捕获检查异常,而无需知道被调用者必须通过不允许检查异常的接口传递它。

try {
  // some code that can throw both checked and runtime exception

} catch (Exception e) {
  throw rethrow(e);
}

在调用方法中我可以再次声明检查异常。

public void loadFile(String file) throws IOException {
   // call method with rethrow
}

/**
 * Cast a CheckedException as an unchecked one.
 *
 * @param throwable to cast
 * @param <T>       the type of the Throwable
 * @return this method will never return a Throwable instance, it will just throw it.
 * @throws T the throwable as an unchecked throwable
 */
@SuppressWarnings("unchecked")
public static <T extends Throwable> RuntimeException rethrow(Throwable throwable) throws T {
    throw (T) throwable; // rely on vacuous cast
}

处理异常有很多不同的选项。我们使用其中的一些。

https://vanilla-java.github.io/2016/06/21/Reviewing-Exception-Handling.html

您可以使用 instanceof 运算符重写相同的内容

try {
    // some code that can throw both checked and runtime exception
} catch (Exception e) {
    if (e instanceof RuntimeException) {
        throw e;
    } else {
        throw new RuntimeException(e);
    }
}

不过,您的解决方案看起来更好。

Guava 的 Throwables.propagate() 正是这样做的:

try {
    // some code that can throw both checked and runtime exception
} catch (Exception e) {
    throw Throwables.propagate(e);
}

更新:此方法现已弃用。有关详细说明,请参阅 this page

问题是 Exception 太宽泛了。您应该确切知道可能的已检查异常是什么。

try {
    // code that throws checked and unchecked exceptions
} catch (IOException | SomeOtherException ex) {
    throw new RuntimeException(ex);
}

这不起作用的原因揭示了应该解决的更深层次的问题:

如果一个方法声明它 throws Exception 那么它就太宽泛了。知道 "something can go wrong" 没有更多信息对调用者没有用。该方法应该在有意义的层次结构中使用特定的异常 类,或者在适当的情况下使用未经检查的异常。

如果一个方法抛出太多不同种类的检查异常,那么它就太复杂了。它应该被重构为多个更简单的方法,或者异常应该被安排在一个合理的继承层次结构中,视情况而定。

当然也有例外。如果某种横切框架(例如 JUnit 或 AspectJ 或 Spring)使用方法而不是包含一个 API 供其他人使用,则声明方法 throws Exception 可能是完全合理的。

我通常使用相同类型的代码结构,但在三元运算符实际上使代码更好的几次之一中将其压缩为一行:

try {
  // code that can throw
}
catch (Exception e) {
  throw (e instanceof RuntimeException) ? (RuntimeException) e : new RuntimeException(e);
}

这不需要额外的方法或 catch 块,这就是我喜欢它的原因。

我有一个专门编译的 .class 文件,其中包含以下内容:

public class Thrower {
    public static void Throw(java.lang.Throwable t) {
        throw t;
    }
}

它很管用。 java 编译器通常会拒绝编译它,但字节码验证器根本不在乎。

class 的用法类似于 Peter Lawrey 的回答:

try {
  // some code that can throw both checked and runtime exception

} catch (Exception e) {
    Thrower.Throw(e);
}

lombok 已通过方法上的简单注释处理此问题

示例:

import lombok.SneakyThrows;

@SneakyThrows
void methodThatUsusallyNeedsToDeclareException() {
    new FileInputStream("/doesn'tMatter");
}

在示例中,方法应该声明 throws FileNotFoundException,但使用 @SneakyThrows 注释时,它没有声明。

在幕后实际发生的是 lombok 对同一个问题使用与 相同的技巧。

任务完成!