Java:异常作为控制流?

Nic*_*ner 10 java exception control-flow

我听说使用控制流异常是不好的做法.你觉得这怎么样?

public static findStringMatch(g0, g1) {

    int g0Left = -1;
    int g0Right = -1;
    int g1Left = -1;
    int g1Right = -1;

//if a match is found, set the above ints to the proper indices
//...
//if not, the ints remain -1

        try {
            String gL0 = g0.substring(0, g0Left);
            String gL1 = g1.substring(0, g1Left);

            String g0match = g0.substring(g0Left, g0Right);
            String g1match = g1.substring(g1Left, g1Right);

            String gR0 = g0.substring(g0Right);
            String gR1 = g1.substring(g1Right);

            return new StringMatch(gL0, gR0, g0match, g1match, gL1, gR1);
        }
        catch (StringIndexOutOfBoundsException e) {
            return new StringMatch(); //no match found
        }
Run Code Online (Sandbox Code Playgroud)

因此,如果未找到匹配项,则整数将为-1.当我尝试获取子字符串时,这将导致异常g0.substring(0, -1).然后该函数返回一个对象,表明找不到匹配项.

这是不好的做法吗?我可以手动检查每个索引以查看它们是否全部为-1,但这感觉更像是工作.

UPDATE

我删除了try-catch块并将其替换为:

    if (g0Left == -1 || g0Right == -1 || g1Left == -1 || g1Right == -1) {
        return new StringMatch();
    }
Run Code Online (Sandbox Code Playgroud)

哪个更好:检查每个变量是否为-1,或者使用布尔值foundMatch来跟踪并在最后检查它?

Yan*_*ton 17

通常例外是昂贵的操作,正如名称所暗示的那样,特殊条件.因此,在控制应用程序流程的上下文中使用它们确实被认为是不好的做法.

特别是在您提供的示例中,您需要对您为StringMatch构造函数提供的输入进行一些基本验证.如果它是一个返回错误代码的方法,以防一些基本参数验证失败,你可以避免事先检查,但事实并非如此.

  • +1:同意.Joshua Bloch在Effective Java中提出了相同的建议.最好先显式测试你的参数. (6认同)

Kev*_*Day 10

我已经做了一些测试.在现代JVM上,它实际上不会影响运行时性能(如果有的话).如果在打开调试的情况下运行,那么它确实会大大降低速度.

有关详细信息,请参阅以

(我还应该提到,我仍然认为这是一种不好的做法,即使它不会影响性能.最重要的是,它反映了可能很难测试的可能很差的算法设计)


Sin*_*hot 6

是的,这是一种不好的做法,特别是当你有办法避免异常时(在尝试索引之前检查字符串长度).Try和catch块旨在将"普通"逻辑与"异常"和错误逻辑分开.在您的示例中,您已将"正常"逻辑扩展到异常/错误块中(未找到匹配也不例外).您也在滥用,substring因此您可以利用它产生的错误作为控制流.


小智 6

程序流程应尽可能直线(因为即使应用程序变得相当复杂),也要使用标准控制流程结构.接触代码的下一个开发人员可能不是您,并且(正确地)误解了您使用异常而非条件来确定控制流的非标准方式.

在一些遗留代码重构期间,我现在正在针对这个问题稍微倾斜一点.

我用这种方法发现的最大问题是使用try/catch打破了正常的程序流程.

在我正在处理的应用程序中(这与您应用的示例不同),异常用于在方法调用内进行通信,发生给定结果(例如查找帐号但未找到它).这会在客户端创建意大利面条代码,因为调用方法(在非异常事件或正常用例事件期间)会突破它在调用之前执行的任何代码以及catch块.这在一些非常长的方法中重复了很多次,使代码很容易被误读.

对于我的情况,一个方法应该返回每个签名的值,除了真正特殊的事件.异常处理机制旨在发生异常时采用另一个路径(尝试从方法中恢复,以便仍然可以正常返回).

在我看来,如果你非常紧密地调整try/catch块的范围,你就可以做到这一点; 但我认为这是一个坏习惯,并且可能导致代码很容易被误解,因为调用代码会将任何抛出的异常解释为'GOTO'类型的消息,从而改变程序流程.我担心虽然这种情况不属于这个陷阱,但这样做往往会导致编码习惯导致我现在生活的噩梦.

那场噩梦并不令人愉快.