LBYL vs EAFP在Java中?

我最近在教自己的Python,并发现LBYL / EAFP成语在错误检查之前执行代码。 在Python中,EAFP似乎是被接受的风格,而且它似乎与这个语言一起工作得很好。

LBYL(在你之前):

def safe_divide_1(x, y): if y == 0: print "Divide-by-0 attempt detected" return None else: return x/y 

EAFP( 它比P ermission更安全 ):

 def safe_divide_2(x, y): try: return x/y except ZeroDivisionError: print "Divide-by-0 attempt detected" return None 

我的问题是:我从来没有听说过使用EAFP作为主要的数据validation结构,来自Java和C ++的背景。 EAFP是在Java中使用明智的东西吗? 还是有太多的例外开销? 我知道只有在实际抛出exception时才会有开销,所以我不确定为什么不使用EAFP的简单方法。 这只是偏好吗?

就个人而言,我认为这是按惯例支持的,EAFP从来不是一个好的select。 你可以把它看作是相当于以下内容:

 if (o != null) o.doSomething(); else // handle 

而不是:

 try { o.doSomething() } catch (NullPointerException npe) { // handle } 

此外,请考虑以下几点:

 if (a != null) if (b != null) if (c != null) a.getB().getC().doSomething(); else // handle c null else // handle b null else // handle a null 

这可能看起来不那么优雅(是的,这是一个简单的例子 – 与我一起),但它给你更大的处理错误的粒度,而不是把它全部包装在try-catch中以获得NullPointerException ,然后试图找出你在哪里,为什么你得到它。

我看到它的方式永远不会被使用,除了极less数的情况。 另外,由于您提出了这个问题: 是的,即使不抛出exception,try-catch块也会产生一些开销

如果您正在访问文件,EAFP比LBYL更可靠,因为涉及LBYL的操作不是primefaces操作,并且文件系统可能会在您查看时间和跳跃时间之间发生变化。 其实,标准名称是TOCTOU – 检查时间,使用时间, 错误检查导致的错误是TOCTOU错误。

考虑创build一个必须具有唯一名称的临时文件。 找出所选文件名是否存在的最好方法是尝试创build它 – 确保使用选项来确保在文件已经存在的情况下操作失败(以POSIX / Unix的forms,用O_EXCL标志open() )。 如果您尝试testing文件是否已经存在(可能使用access() ),那么在表示“否”的时间和您尝试创build文件的时间之间,某人或其他人可能已经创build了该文件。

相反,假设您尝试读取现有的文件。 你检查文件是否存在(LBYL)可能会说“它在那里”,但是当你真的打开它时,你会发现“它不存在”。

在这两种情况下,你必须检查最后的操作 – LBYL不会自动帮忙。

(如果你搞乱了SUID或者SGID程序, access()提出一个不同的问题;这可能与LBYL有关,但是代码仍然要考虑到失败的可能性。

除了Python和Java中相对的例外成本之外,请记住,它们之间的哲学/态度有所不同。 Java试图对types(和其他所有东西)非常严格,要求显式的,详细的类/方法签名声明。 它假设你在任何时候都应该知道你正在使用什么types的对象,以及它能做什么。 相比之下,Python的“鸭子打字”意味着你不知道(而且不应该关心)一个对象的清单types是什么,你只需要关心它在你提出问题的时候是否嘎嘎。 在这种宽容的环境下,唯一的理智的态度就是假定事情会发挥作用,但是如果不行的话,要做好准备应付的后果。 Java的自然限制并不适合这样一个偶然的方法。 (这不是为了贬低方法或语言,而是说这些态度是每种语言成语的一部分,在不同的语言之间复制习语往往会导致尴尬和不良的交stream……)

Python中的exception处理比Java中更有效,至less部分原因是你看到Python中的构造。 在Java中,以这种方式使用exception效率更低(就性能而言)。

考虑这些代码片段:

 def int_or_default(x, default=0): if x.isdigit(): return int(x) else: return default def int_or_default(x, default=0): try: return int(x) except ValueError: return default 

他们都看起来是正确的,对吗? 但其中一个不是。

前者使用LBYL,由于isdigitisdecimal之间的细微差别而isdecimal ; 当用string“①²³🄅₅”调用时,它会抛出一个错误,而不是正确地返回默认值。

后来,使用EAFTP,根据定义,导致正确的处理。 没有行为不匹配的范围,因为需要这些要求的代码是断言该要求的代码。

使用LBYL意味着采取内部逻辑并将其复制到每个呼叫站点。 您不需要对您的需求进行一个规范的编码,而是每次调用该函数时都有一个自由的机会。

值得注意的是,EAFTP 不是关于exception的,Java代码尤其不应该普遍地使用exception。 这是关于正确的工作,以正确的代码块。 例如,使用Optional返回值是编写EAFTP代码的完全有效的方式,对于确保正确性比LBYL更有效。