Why is “else” rarely used after “if x then return”?
这种方法:
1 2 3 4 5 6 7 8 | boolean containsSmiley(String s) { if (s == null) { return false; } else { return s.contains(":)"); } } |
可以等效地写:
1 2 3 4 5 6 7 |
在我的经验中,第二种形式更常见,尤其是在更复杂的方法中(可能有几个这样的出口点),对于"抛出"和"返回"也是如此。然而,第一种形式可以使代码的条件结构更加明确。有什么理由比另一个更喜欢一个吗?
(相关:一个函数应该只有一个返回语句吗?)
在这种情况下,
根据我的经验,这取决于代码。如果我在"保护"什么,我会:
1 2 3 4 5 | if (inputVar.isBad()) { return; } doThings(); |
要点很清楚:如果该语句为假,我不希望函数继续。
另一方面,有一些函数有多个选项,在这种情况下,我会这样写:
1 2 3 4 5 6 7 | if (inputVar == thingOne) { doFirstThing(); } else if (inputVar == secondThing) { doSecondThing(); } else { doThirdThing(); } |
即使可以写为:
1 2 3 4 5 6 7 8 9 10 | if (inputVar == thingOne) { doFirstThing(); return; } if (inputVar == thingTwo) { doSecondThing(); return; } doThingThree(); return; |
它实际上是以哪种方式最清楚地显示代码在做什么(不一定是哪一位代码最短或缩进最少)。
这是一个名为guard子句的模式。其思想是预先进行所有检查,以减少嵌套条件以提高可读性。
从链接:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | double getPayAmount() { double result; if (_isDead) { result = deadAmount(); } else { if (_isSeparated) { result = separatedAmount(); } else { if (_isRetired) { result = retiredAmount(); } else { result = normalPayAmount(); } } } return result; } |
使用guard子句,您将看到以下结果:
1 2 3 4 5 6 7 | double getPayAmount() { if (_isDead) return deadAmount(); if (_isSeparated) return separatedAmount(); if (_isRetired) return retiredAmount(); return normalPayAmount(); }; |
你会看到这一切:
1 2 3 4 5 6 | if (condition) { return var; } // by nature, when execution reaches this point, condition can only be false, // therefore, the else is unnecessary return other_var; |
在大多数情况下,添加else子句不仅是不必要的,而且在很多情况下,它会被编译器优化掉。
想想计算机是如何看待这段代码的(就机器代码而言,为了演示,这里简化为伪代码):
1 2 3 4 5 6 | 0x00: test [condition] 0x01: if result of test was not true, goto [0x04] 0x02: push [var] onto stack 0x03: goto [0x05] 0x04: push [other_var] onto stack 0x05: return from subroutine |
代码(同样,这是一个伪代码,而不是程序集)对
对于许多人来说,拥有一个函数的多个可能的出口点被认为是不好的和/或令人困惑的实践,因为程序员必须考虑通过他的代码的每一条可能的路径。另一种做法是:
1 | return (condition) ? var : other_var; |
这简化了代码,并且不会创建任何新的出口点。
我喜欢这样写:
1 2 3 |
与任何有关编码样式的"讨论"一样,没有正确的答案。我倾向于应用这些考虑:
代码是否在所有情况下都按预期工作。(最小惊喜原则)
下一个开发人员(我自己或其他人)能否理解它在做什么以及为什么。
关于变化的代码有多脆弱。
是简单的,因为它需要,而不是更多。也就是说,不在工程上或工程下。
一旦我很高兴我已经满足了以上的要求,其余的通常都只是顺其自然。
我更喜欢第一个选项,因为它更容易被人阅读。
作为一个类比,比较下面两句话:"如果今天下雨,那就带伞,否则就带太阳镜。""如果今天下雨,那就带伞,带太阳镜"。第一句话对应于问题的第一段代码,第二段代码对应于第二段代码。第一个更清晰易读,不是吗?
其他人可能已经注意到了这一点,但我建议不要在通常需要字符串的地方使用空值。如果您真的希望检查以防止有人传递空值,则可以使用断言(在开发时)或单元测试(部署):
1 2 3 4 | boolean containsSmiley(String s) { assert s != null :"Quit passing null values, you moron."; return s.contains(":)"); } |
我换了一条经验法则:永远不要。曾经。传递空值,除非外部API调用明确要求它。第二:如果外部方法可能返回空值,请将其替换为合理的非空值(例如空字符串),或者添加一个整洁的检查。我厌倦了重复的
但这有点离题。我喜欢在顶部加上简短的条件和保护子句,如果程序流指示ELSE永远不会到达那里,就删除ELSE。
这是宗教上的争论,一天结束的时候,这无关紧要。我甚至认为第一种形式在某些情况下更易于阅读。如果在
1 2 3 4 5 6 7 8 9 10 11 | if (s == null) { return false; } else if (s.Contains(":))")) { return true; } else if (s.Contains(":-(")) { return false; } return s.contains(":)"); |
奥卡姆的剃刀是这样一个原则:"实体不应超越必要性而成倍增加。"
不过,在大多数情况下,我更喜欢代码在其意图中尽可能地明确。如果您可以对您的函数进行重构,使其更易于他人阅读,那么就进行重构。作为一个专业的程序员,你的目标应该是为那些需要在你之后维护你的代码的人编程(包括你自己2年后…)。你能为他们做的任何事都是值得做的。
因为它更好。你知道你也可以用来创建多个层次的嵌套,但是没有人真正做到这一点。
因为Eclipse中有一个可选(默认情况下关闭)警告,如果在这种情况下使用了else;)。
嗯,有些原因只是惯例,但有一个优势,以上述形式…
在对返回语句进行编码时,通常会将最后一个语句设置为默认返回值。这主要有助于重构——否则,子句往往会被其他结构所包围,或者可能会意外地深入到树中。
从维护的角度来看,我更喜欢一个出口点而不是多个出口点。可以在一个出口点修改(或修饰)最终结果,而不是在n个出口点修改(或修饰)。
第二种形式,如果更简单/更短。这并不总是意味着更清楚。我建议你做你认为最清楚的事。我个人会写信。
1 2 3 |
虽然else是正确的,并且在逻辑和可运行性方面没有任何问题,但是我喜欢避免在函数没有超出if/else范围的返回语句时出现最初的wtf时刻。
因为这和写下面其中一篇文章一样,它带来了关于程序员意图的证据:
1 2 3 4 5 6 | boolean containsSmiley(String s) { if (s == null) // The curly braces are not even necessary as the if contains only one instruction. return false; return s.contains(":)"); } |
甚至这个:
1 2 3 |
这两种形式是:
在我看来,第二个更合理。它更像是一个"默认"动作,就像一个开关。如果它不匹配任何其他出口点,则执行该操作。你真的不需要别的。我会说,如果整个函数只有if和else if,那么这里的else就有意义了,因为它是一个巨大的条件。如果有多个条件和其他函数在其中运行,那么将使用末尾的默认返回。
我赞成可读性。如果您正在扫描代码屏幕,试图找出代码的作用,那么这对开发人员来说是一个视觉提示。
…但实际上并不需要,因为我们都对代码进行了很好的注释,对吧?:)
第一种形式简单地不那么冗长——当您返回一个值时,您会自动离开您所在函数的作用域并返回给调用者,因此只有当if语句的计算结果不为true并且随后返回任何内容时,之后的任何代码才会执行。
正如你所看到的,不同的人对可读性有不同的看法。有些人认为代码行数越少,代码的可读性就越强。其他人认为第二种形式的对称性使其更具可读性。
我的看法是,可能两种观点都是正确的…为了那些抱着他们的人。其推论是,您不能编写所有人都认为最可读的代码。因此,最好的建议是遵循您的强制编码标准所规定的操作(如果它对此有任何规定),并且通常使用您的常识。(如果你背负着一些高声叫嚷的白痴,他们坚持认为他的方法是"正确的"……随波逐流。)