33

我有一些代码可能会同时引发检查异常和运行时异常。

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

我的解决方案有点开销,而且不是“整洁”:

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

任何想法更优雅的方式?

4

8 回答 8

33

我使用“盲目”重新抛出检查异常。我已经使用它来通过 Streams API,在该 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

于 2016-09-27T08:11:54.500 回答
19

番石榴Throwables.propagate()正是这样做的:

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

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

于 2016-09-27T08:33:29.537 回答
4

并不真地。

如果你经常这样做,你可以把它塞进一个辅助方法中。

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);
}
于 2016-09-27T08:10:14.923 回答
2

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

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

它只是工作。java 编译器通常会拒绝编译它,但字节码验证器根本不关心。

该类的使用类似于彼得劳里的回答:

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

} catch (Exception e) {
    Thrower.Throw(e);
}
于 2016-09-27T15:46:24.723 回答
1

您可以使用 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);
    }
}

但是,您的解决方案看起来更好。

于 2016-09-27T08:17:08.087 回答
1

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

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

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

如果一个方法声明它,throws Exception那么它就太宽泛了。在没有进一步信息的情况下知道“可能会出错”对呼叫者没有用处。该方法应该在有意义的层次结构中使用特定的异常类,或者在适当的情况下使用未经检查的异常。

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

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

于 2016-09-27T10:37:37.247 回答
1

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

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

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

于 2016-09-27T15:07:12.077 回答
0

lombok 通过对方法的简单注释处理了这个问题

例子:

import lombok.SneakyThrows;

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

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

幕后实际发生的事情是,lombok 与对同一问题的高评价答案相同。

任务完成!

于 2021-12-01T05:39:44.667 回答