Language agnostic 在流程之前进行验证更好,还是只抛出异常更好?

Language agnostic 在流程之前进行验证更好,还是只抛出异常更好?,language-agnostic,error-handling,Language Agnostic,Error Handling,考虑以下代码: x = Repo.GetX(13); Services.Process(x); 我可以在处理它之前检查x是否为null,或者我可以使用Services.Process()在x为null时抛出异常并使用try/catch块 哪种方法更可取 (或者我应该两者都做,只是在例外情况下,x从检索到处理它都以某种方式变为空?检查要好得多。异常处理通常很昂贵。像往常一样,这类问题的正确答案是:视情况而定 如果可能,最好的方法是定义执行此类检查的边界。边界内的所有内容都可以依赖于所有内容的良好

考虑以下代码:

x = Repo.GetX(13);
Services.Process(x);
我可以在处理它之前检查x是否为null,或者我可以使用Services.Process()在x为null时抛出异常并使用try/catch块

哪种方法更可取


(或者我应该两者都做,只是在例外情况下,x从检索到处理它都以某种方式变为空?

检查要好得多。异常处理通常很昂贵。

像往常一样,这类问题的正确答案是:视情况而定

如果可能,最好的方法是定义执行此类检查的边界。边界内的所有内容都可以依赖于所有内容的良好初始化。如果仍然有空的潜入,用NPE引爆是非常合适的

通常,在尝试执行实际工作之前检查先决条件会使代码更加清晰

如果您无法在系统中建立这些边界,那么问题是:当x为NULL且结果异常未被处理时会发生什么,当您不处理它时,您有什么选择

在许多情况下,您无论如何也不能对此做太多,然后让异常从调用堆栈上升到ui(或服务接口或其他任何东西)也没关系


但有时使用空参数调用进程会破坏硬件,杀死患者,或者真的很糟糕。在这种情况下:在潜在的有害操作之前进行检查。

应使用异常处理来处理异常


如果您认为x不应该为null,请使用try/catch语句。Ifx在某些情况下将为空,请使用和If语句。

如果您试图在以下两者之间做出选择:

x = Repo.GetX(13);
try {
    Services.Process(x);
} catch (NullPointerException) {
    System.err.println('x is NULL');
}

然后要意识到它们的意思并不相同。第一个捕获由
进程
引发的任何NullPointerException。第二个仅处理
x
本身为NULL的情况-如果
Process
执行
x.foo().bar()
,并且
x.foo()
返回NULL,则不处理该情况

你应该根据你能处理的错误在两者之间做出选择

Services.Process
被记录为抛出NullPointerException的情况下,当且仅当
x
为NULL时,因此(假设我们相信该文档)这两者的含义相同,那么我将执行第二个。这样,如果文档是假的,并且
Services.Process
中有一个bug导致它在其他情况下抛出,那么我们就不会错误地处理/报告异常,就好像
x
是空的,而实际上它不是空的

因此,在两种情况下捕获并抑制异常:

  • 你知道是什么(什么样的事情)导致了错误,你知道你可以处理这种情况并继续不管
  • 保护某些相当外层的代码(如事件循环)不受程序或环境中意外错误的影响。理想情况下,这将导致大量日志记录,因为要么系统未能完成您预期的任务(可能是由于缺乏资源或未满足的依赖关系),要么某人的代码中存在错误
  • 不要仅仅因为知道一种可能导致异常的情况,并且可以处理这种情况,就捕捉到可能由于其他原因引发相同的异常。我想你可以做到:

    x = Repo.GetX(13);
    try {
        Services.Process(x);
    } catch (NullPointerException) {
        if (x == NULL) {
            System.err.println('x is NULL');
        } else {
            throw;
        }
    }
    
    但在这种情况下,这似乎是不必要的冗长:因为我们可以提前检查
    x
    是否为空,所以我们也可以这样做。但是,在某些情况下,您捕获异常是因为不可能或只是愚蠢地试图提前预测它是否会发生:

    String s = Rep.GetString(13);
    try {
        f = Float.parseFloat(s);
        System.out.println('is a number');
    } catch (NumberFormatException) {
        System.out.println('not a number');
    }
    
    在调用
    parseFloat
    之前,我们可以检查
    s
    是否是一个有效的数字,但这基本上需要做两次相同的工作-检查字符串是否是有效的float与实际转换它几乎是一样的。所以抓住这个例外是有意义的——我们知道它意味着什么,我们知道该怎么做,我们不可能现实地阻止它发生


    (或者您可以使用DecimalFormat.parse,它根本不会抛出任何错误)

    只要这段代码不是在一个巨大的循环中运行,就不应该考虑性能。如果它确实在紧循环中运行,那么应该进行性能测试,并基于这些测试来调整实现。当“异常”情况很常见时,异常处理通常是昂贵的。如果一千次中只有一次发生,实际上跳过检查并抛出异常会更快。正如另一个答案所说,这要视情况而定。除了性能,这里还有其他需要担心的事情。
    String s = Rep.GetString(13);
    try {
        f = Float.parseFloat(s);
        System.out.println('is a number');
    } catch (NumberFormatException) {
        System.out.println('not a number');
    }