简体   繁体   English

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

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

I have some code that might throw both checked and runtime exceptions.我有一些代码可能会同时抛出已检查和运行时异常。

I'd like to catch the checked exception and wrap it with a runtime exception.我想捕获已检查的异常并将其包装为运行时异常。 But if a RuntimeException is thrown, I don't have to wrap it as it's already a runtime exception.但是如果抛出了 RuntimeException,我就不必包装它,因为它已经是一个运行时异常。

The solution I have has a bit overhead and isn't "neat":我的解决方案有一点开销并且不“整洁”:

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

Any idea for a more elegant way?任何想法更优雅的方式?

I use a "blind" rethrow to pass up checked exceptions. 我使用“盲目”重新抛出来传递已检查的异常。 I have used this for passing through the Streams API where I can't use lambdas which throw checked exceptions. 我已经使用它来传递Streams API,我不能使用抛出已检查异常的lambdas。 eg We have ThrowingXxxxx functional interfaces so the checked exception can be passed through. 例如,我们有ThrowingXxxxx功能接​​口,因此可以传递经过检查的异常。

This allows me to catch the checked exception in a caller naturally without needing to know a callee had to pass it through an interface which didn't allow checked exceptions. 这允许我自然地在调用者中捕获已检查的异常,而不需要知道被调用者必须通过不允许检查异常的接口传递它。

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

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

In a calling method I can declare the checked exception again. 在调用方法中,我可以再次声明已检查的异常。

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
}

There is a lot of different options for handling exceptions. 处理异常有很多不同的选择。 We use a few of them. 我们使用其中的一些。

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

Guava's Throwables.propagate() does exactly this: Guava的Throwables.propagate()这样做的:

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

UPDATE: This method is now deprecated. 更新:此方法现已弃用。 See this page for a detailed explanation. 有关详细说明,请参阅此页面

Not really. 并不是的。

If you do this a lot, you could tuck it away into a helper method. 如果你经常这么做,你可以把它塞进一个辅助方法。

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);
}

I have a specially compiled .class file containing the following: 我有一个特殊编译的.class文件,其中包含以下内容:

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

It just works. 它只是有效。 The java compiler would normally refuse to compile this, but the bytecode verifier doesn't care at all. java编译器通常会拒绝编译它,但字节码验证器根本不关心。

The class is used similar to Peter Lawrey's answer: 该课程的使用类似于Peter Lawrey的答案:

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

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

You can rewrite the same using instanceof operator 您可以使用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);
    }
}

However, your solution looks better. 但是,您的解决方案看起来更好

The problem is that Exception is too broad. 问题是Exception太宽泛了。 You should know exactly what the possible checked exceptions are. 您应该确切知道可能的已检查异常是什么。

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

The reasons why this wouldn't work reveal deeper problems that should be addressed instead: 这不起作用的原因揭示了应该解决的更深层次的问题:

If a method declares that it throws Exception then it is being too broad. 如果一个方法声明它throws Exception那么它太宽泛了。 Knowing that "something can go wrong" with no further information is of no use to a caller. 在没有进一步信息的情况下知道“某些事情可能出错”对于呼叫者来说是没有用的。 The method should be using specific exception classes in a meaningful hierarchy, or using unchecked exceptions if appropriate. 该方法应该在有意义的层次结构中使用特定的异常类,或者在适当的情况下使用未经检查的异常。

If a method throws too many different kinds of checked exception then it is too complicated. 如果一个方法抛出了太多不同类型的已检查异常,那么它就太复杂了。 It should either be refactored into multiple simpler methods, or the exceptions should be arranged in a sensible inheritance hierarchy, depending on the situation. 它应该被重构为多个更简单的方法,或者异常应该根据情况安排在合理的继承层次结构中。

Of course there can be exceptions to the rule. 当然,规则可能有例外。 Declaring a method throws Exception can be perfectly reasonable if it's consumed by some kind of cross-cutting framework (such as JUnit or AspectJ or Spring) rather than comprising an API for others to use. 声明一个方法throws Exception如果被某种交叉框架(例如JUnit或AspectJ或Spring)使用而不是包含供其他人使用的API,那么它是完全合理的。

I generally use the same type of code structure, but condense it down to one line in one of the few times a ternary operator actually makes code better: 我通常使用相同类型的代码结构,但在三元运算符实际使代码更好的几次中将其压缩到一行:

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

This does not require additional methods or catch blocks which is why I like it. 这不需要额外的方法或catch块,这就是我喜欢它的原因。

lombok has this handled with a simple annotation on the method 😊 lombok 通过对方法的简单注释来处理此问题😊

Example:例子:

import lombok.SneakyThrows;

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

In the example the method should have declared throws FileNotFoundException , but with the @SneakyThrows annotation, it doesn't.在示例中,该方法应该声明throws FileNotFoundException ,但使用@SneakyThrows注释,它没有。

What actually happens behind the scenes is that lombok does the same trick as the high rated answer to this same question.在幕后实际发生的事情是 lombok 对同一问题的高评价答案做了同样的伎俩。

Mission accomplished!任务完成!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM