Java: 如何在处理异常时传播“方法退出”?

huangapple go评论112阅读模式
英文:

Java: How to propagate a "method exit" when tucking-in exceptions?

问题

 * 通过方法退出 - 我的意思是方法中的操作比如```return``````throw new...```,编译器认为这是方法的结束 - 如果您可以告诉我方法退出的正确表达方式我会编辑问题

我的问题如下
- 我经常使用```throw new RuntimeException(...```。
- 因此我决定将它隐藏起来”,如下
    ```public static void quickRaise(String msg) { throw new RuntimeException(msg); }``` <BR>
    
    然后我可以重复使用它<BR>
    将来这将有助于我改进围绕引发运行时异常的过程甚至可以切换到自定义异常类而无需在代码中搜索异常抛出的位置

- 然而在以前我可以这样编写

        public MyType doSomething() {
            try {
                //...
                return new MyType(parameter);
            } catch (Exception e) {
                throw new RuntimeException("msg")
            }
        }
    编译器会正确理解这个方法通过return或throw退出”,因此没有逻辑死路”。

当我将```throw new RuntimeException("msg")```更改为```quickRaise("msg")```编译器不再认为我的方法完成”。它抱怨缺少return语句即使```quickRaise```在语义上等同于```throw```(或者至少是我尝试这么做!)

让我试着通过一个可复现的示例来重申这个问题这将无法编译这就是问题所在):

    public static void main(String[] args) {
        System.out.println(doSomething());
    }
    public static String doSomething() {
        try {
            //... 这里进行一些有趣的操作
            return "Something";
        } catch (Exception e) {
            quickRaise("无法找到握手");
            //throw new RuntimeException("如果取消对此行的注释,它将会编译!");
        }
    }
    public static void quickRaise(String msg) {
        throw new RuntimeException(msg);
    }
英文:
  • By "method exit" - I mean the actions in a method such as return or throw new... that the compiler considers the end of a method - if you could please tell me the accepted word for "method exit", I will edit the question

My problem is the following:

  • I do a lot of throw new RuntimeException(...

  • So, I decided to "tuck it in" as:
    public static void quickRaise (String msg) { throw new RuntimeException(msg); } <BR>

    And then I can reuse it.<BR>
    (This will help me in the future to enhance the procedure around raising Runtime Exceptions and even
    switch to a custom Exception class, without fishing in the code for exception throws)

  • However, where before I could write:

      public MyType doSomething() {
    try {
    //...
    return new MyType (parameter);
    } catch (Exception e) {
    throw new RuntimeException(&quot;msg&quot;)
    }
    }
    

    And the compiler would correctly understand that "this method either exits by return or by throw" and therefore there are no logical "dead ends"

When I changed throw new RuntimeException(&quot;msg&quot;) to quickRaise(&quot;msg&quot;), the compiler no longer considers my method "complete". It complains about a missing return statement, even though quickRaise is semantically equivalent to throw (or at least this is what I am trying to do!)

Let me try to reiterate the problem by a reproductive example (this will not compile, which is the problem):

public static void main(String[] args) {
System.out.println(doSomething());
}
public static String doSomething () {
try {
//... Some fun stuff going on here
return &quot;Something&quot;;
} catch (Exception e) {
quickRaise(&quot;Could not find handshakes&quot;);
//throw new RuntimeException(&quot;If you uncomment this line, it will compile!&quot;);
}
}
public static void quickRaise (String msg) {
throw new RuntimeException(msg);
}

答案1

得分: 2

你的想法是极不明智的。

例如,这只是糟糕的代码风格:

try {
   someIO();
} catch (IOException e) {
   throw new RuntimeException("Problem with IO");
}

糟糕的地方在于你现在已经丧失了有关问题的实际信息。这些信息被锁定在异常的五个单独部分中:它的类型(例如,FileNotFoundException),它的消息(例如,“目录/foo/bar不存在”),它的堆栈跟踪,它的因果链,以及作为可抛出对象,该特定类型异常的任何额外细节(例如,某些 SQLException 的 DB 引擎特定错误编码)。

丢弃这些信息是愚蠢的。

要修复这个问题,你只需要添加原因:

} catch (IOException e) {
    throw new RuntimeException("IO problem", e);
}

现在,IOException 被标记为你要抛出的异常的 原因,这意味着在错误日志中,你将会看到它 + 消息 + 它的堆栈跟踪 + 任何它可能有的原因的堆栈跟踪。

要让编译器意识到方法在这里结束,你只需要将它抛出:

public static RuntimeException quickRaise(String msg) {
    throw new RuntimeException(msg);
    return null; // 无所谓,我们永远不会执行到这里
}

// 使用方式:

throw quickRaise(msg);

但是,正如我之前解释过的,这是一个非常糟糕的想法。

其次,拥有“我只想抛出一个异常,也许以后我想要替换我抛出的异常类型”的想法也并不现实:你需要为情况选择一个适当的异常,因此你无法一开始就编写一个适用于所有情况的抛出方法。

那么,我该怎么做?

首先,学会接受 throws 子句。如果你的方法从根本上执行 I/O 操作(例如,它的 javadoc 和/或名称明显表明,例如 saveGame(Path p)scanUserHome),那么它应该声明为 throws IOException

如果你的方法是一个入口点(也就是说,它是你的代码开始运行的第一个点),那么你的方法应该声明为 throws Exception。例如,你的 public static void main() 方法应该 throws Exception。有时入口点不是 main 方法,而是其他什么东西(例如 web 处理程序路由钩子),有时候反向的愚蠢框架阻止你这样做,但通常会有包装功能(例如 } catch (Exception e) { throw new ServletException(e); })。

对于既不是方法目的的一部分,更多是实现细节的异常,又非常不太可能出错,而且除了硬崩溃之外没有太多其他选择的情况,是的,可以重新包装为 RuntimeException。对于所有这样的异常来说,很少有在全局范围内进行更改的意义。最多,你可能会事后意识到失败的可能性比你最初想象的要大一些,然后为其创建一个适当的异常并记录此行为。但这又是基于每个方法的基础,而不是一种可以套用的通用方法。

英文:

Your idea is highly inadvisable.

For example, this is just bad codestyle:

try {
someIO();
} catch (IOException e) {
throw new RuntimeException(&quot;Problem with IO&quot;);
}

The reason it's bad is that you have now obliterated the actual information about the problem. That information is locked into 5 separate parts of that exception you just caught: Its type (for example, FileNotFoundException, its message (e.g. "Directory /foo/bar does not exist"), its stack trace, its causal chain, and as throwables are objects, any particular extra detail for that particular kind of exception (such as the DB-engine-specific error coding for some SQLException).

Throwing this info away is silly.

All you'd need to do to fix this, is to add the cause:

} catch (IOException e) {
throw new RuntimeException(&quot;IO problem&quot;, e);
}

Now the IOException is marked as the cause of the exception you are throwing, which means in error logs you'll see it + the message + the stack trace of it + the stack trace of any causes it had as well.

All you need to do to make the compiler realize that the method ends here, is to throw it:

public static RuntimeException quickRaise(String msg) {
throw new RuntimeException(msg);
return null; // doesn&#39;t matter, we never get here
}
// to use:
throw quickRaise(msg);

But, as I explained before, this is a very bad idea.

Secondarily, having the idea of 'I just want to throw an exception and maybe later I want to replace the kind of exception I throw' also doesn't really work out: You need to pick a proper exception for the situation, therefore you cannot write a one-size-fits-all throw method in the first place.

Okay, so what do I do?

Primarily, learn to embrace throws clauses. If your method fundamentally does I/O (for example, the javadoc of it and/or the name makes that obvious, it is for example saveGame(Path p), or scanUserHome), then it should be declared to throws IOException.

If your method is an entrypoint (as in, it is the first point where your own code begins running), then your method should be declared to throws Exception. For example, your public static void main() method should throws Exception. Sometimes an entrypoint isn't main but something else (a webhandler routing hook for example), and sometimes backwards silly franeworks prevent you from doing that, but there tends to be a wrap functionality (such as } catch (Exception e) { throw new ServletException(e); }).

For exceptions which are both [A] fundamentally not part of the method's purpose, but more part of an implementation detail and [B] is very unlikely to go wrong and there's not much you can do other than hard crash if it would, then, yeah, rewrap as RuntimeException. There isn't a lot of point in ever changing this 'globally' for all such exceptions. At best you belatedly realize that failure is a bit more likely than you originally thought and either create a proper exception for it and document this behaviour. But that's, again, on a per-method basis, not something you can apply in blanket fashion.

答案2

得分: 1

你的方法与编译器需要看到流程在抛出语句处终止的需求根本不符。

我建议创建一个实用方法,仅构造一个异常,然后从原始点抛出它。

要么这样做,要么在每次调用quickRaise()之后添加虚拟返回。

英文:

Your approach is fundamentally at odds with the need for the compiler to see that the flow terminates at the throw statement.

I'd suggest having a utility method that just constructs an exception, which you then throw from the original point.

It's either than or put dummy returns after each call to quickRaise().

huangapple
  • 本文由 发表于 2020年10月14日 06:52:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/64344233.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定