String.equals()参数的顺序

26
最近我因为在最近的一个回答中使用了以下内容而收到了一个负评:
String word = ...;
if ("s".equals(word) || "y".equals(word))

由于使用了“Yoda条件”,我收到了一个负面评价。我要求进一步解释,但没有得到任何回应。我更喜欢这种风格,以避免可能的“空指针异常”。
这是一种糟糕的编码风格吗?如果是的话,为什么?

4
我使用 Yoda 表达式是出于个人喜好,和你使用的原因一样。 - mcfinnigan
2
被投票关闭,因为从技术角度来看,只有一点点的差别(你提到的那个),而且有合理的替代方案(显式的null检查),因此辩论不可避免地陷入了风格、一致性、可读性等方面的比较。 - Jon
1
因为我们都知道尤达不说好英语,也许 :-) - Edwin Dalorzo
7
有时候,你会收到一些理解有误的人的负面评价。没人能让我相信应该把常数放在括号里面。你已经有了足够高的评分(实际上比我高很多)来习惯生活中的苦涩 - 无法迎合每个人的心情。 - Boris Strandjev
3
优秀的是 Yoda 条件表达式;我会使用它们。我喜欢它们。if ((null != foo) && ( ! foo.isEmpty)如果 foo 不为 null 且不为空,则执行此条件语句。 - Jim
显示剩余6条评论
10个回答

36

Bill Pugh在Devoxx 2011上提出了这个问题。绝大多数人选择使用形式为"xyz".equals(str)的方式。我支持Bill,现在更喜欢str.equals("xyz")的方式。

Java传统中最基本的原则就是尽早发现错误。NullPointerException异常非常普遍。我们希望能够尽早排除这些空值。

如果你预计引用可能为null,那么我并不反对使用反向符号。明确指定有可能存在null,并在另外的null检查中更容易理解,但是反向顺序应该被充分理解,并且足以将代码与禁止出现null的普通情况区分开来。

在安全领域工作时,一些空值容错问题可能会导致漏洞。


1
@hmjd,此外,这种表示法在其他语言中很常见,但对Java来说不自然。我们有单一职责原则用于类:http://en.wikipedia.org/wiki/Single_responsibility_principle,我们有Curly's Law用于变量:http://www.codinghorror.com/blog/2007/03/curlys-law-do-one-thing.html。因此,一行代码同时执行两个操作(避免nullpointers和检查相等性)对我来说似乎不自然,而且语法不正确,因此更难阅读。这就是为什么你会收到传说中的downvote,这也促使你写下这个问题。 - Nikola Yovchev

10

将常量放在变量之前进行比较的Yoda条件可以被视为不良实践,因为它使代码行难以理解。然而,在这种特定情况下,我认为使用Yoda条件会使代码更易于理解,因为您不需要在其前面加上额外的null检查。


9
访问以下链接了解什么是Yoda条件表示法:Yoda Conditions|Notation 这并不是“糟糕的编码风格”,而是一种不同的编码方式。
在某些语言中,Yoda可以用来追踪拼写错误。我认为-1分并不值得,但这是我的个人意见。
但是如此冗长但非常有趣的文章所解释的那样,使用Yoda也可能会出现问题:Yoda can be bad
到最后,支持者和反对者都有这种符号表示法。

1
在“Yoda can be bad”的评论中,作者指出“foo”.equals(bar)是一种特殊情况,更好的 Yoda 是适用的。 - valbaca

3

这取决于具体情况。如果在您的程序中,“word”永远不应该为null,则使用“word.equals(“s”)”可能更好。如果由于某种鲜为人知的原因“word”将变为null,则会出现NullPointerException。

仔细想一想,如果您得到异常,就知道出了问题,可以更快地发现错误并进行修复。如果程序在静默中继续运行并产生错误结果,则很难检测到问题。实际上,您可能根本没有注意到存在问题。

一切都要看具体情况。


1

你可以写

if (word != null && (word.equals("s") || word.equals("y")))

替代

if ("s".equals(word) || "y".equals(word))

在这种情况下,第一个代码永远不会引起任何NullpointerException,但是在我看来,第二个更好,尽管它是使用了Yoda条件语句

Yoda - 之所以叫Yoda,是因为他说话时会倒着说。 - Jesper
既然接受的回答提到了Bill Pugh,我想我应该补充一下,当我在其他场合听他讲话时,你的第一个(稍微长一些的)建议正是他推荐的。它更明确,告诉你(和null分析引擎)可能为空的word,这是很好甚至至关重要的信息。 - Ole V.V.

1

有几个原因不建议这样做,但最终取决于你(或团队)是否认为这是糟糕的编码风格。反对它的论点包括:

  • 字符串很少为空(而且你不应该创建API,其中它们为空,因为人们不会预料到)
  • 将要比较的值放在第一位感觉很奇怪
  • 代码风格的统一性很重要,因为这种方式是例外,只有当你的团队中每个人都这样做时,你才应该这样做。

正如我所说,我认为这些论点并不是非常强大,也没有理由像你这样做。因此,最重要的是就编码风格达成一致,并坚持下去。


1

TL;DR; 这绝对是不好的编码风格,而不是好的 :D

嗯,在非布尔类型可以评估为布尔值的语言中,尤达条件非常有用,例如

int test = 0;
if ( test ){ /* do something */

但在Java中不允许这样做,因此您不会遇到忘记'='等问题。

if ( test = 2 ){ /* do something */

使用 test == 2 的方式不被编译器所允许。因此,对于那些没有关注过这个问题的人来说,Yoda 表达式可能会显得不自然(因为他/她没有使用除 Java 以外的其他语言)。

  • 这绝对不是糟糕的编码风格,只是在 Java 代码中不太常见。

1
Java仍然允许你编写if (test = true),这当然是更糟糕的风格。 - Jörn Horstmann
我同意,不应该允许任何作业分配。 - scibuff

1

有一种特殊情况的 Yoda 条件语句我在任何答案中都没有看到过被辩护或攻击,所以我会加上它作为参考。这种风格是:

 if(0 < x && x <= max) {

一个 Yoda 条件语句,因为常量(0)在变量(x)之前。反对 Yoda 条件语句的论点是它会影响可读性。将该示例与功能等效的示例进行对比。
 if(x <= max && x > 0) {

你真的认为非 Yoda 变体更易读吗?我不这么认为。

在使用排序关系运算符(<,<=,>,>=)时,为了可读性,我更喜欢以下启发式方法的风格:

  • 使用一致的排序关系:> 与 >= 一致,但与 < 或 <= 不一致;< 与 <= 一致。
  • 优先使用 < 和 <=,因为默认是升序。
  • 如果使用 < 和 <=,则将对变量施加下限的条件放在对上限施加条件之前。如果使用 > 和 >=,则相反。

这往往会产生一个 Yoda 条件来表示下限。


2
我想知道为什么你写了 x <= max && x > 0,而不是更正确的翻译 x > 0 && x <= max,因为你想要代替 Yoda 表达式而不是重新排列表达式。并且 Yoda 表达式仅有一个优点,那就是“0 < x < y”是一种常见的数学方式来描述边界(或“(0, y)”但那是另外一回事 :D)。 - Tom

0
有人可能会认为,你应该对代码进行足够的(单元)测试,以确信 null 值不会进入不应该进入的地方。这样就可以避免使用 Yoda 表达式的必要性。

NPE(空指针异常)非常普遍。全世界都没有足够的单元测试来覆盖空值问题。 - Tom Hawtin - tackline
Yoda条件并不能解决NPE问题。它们只是掩盖了这些问题,使你不那么容易发现它们(有时候)。否则,您当然是正确的,@TomHawtin-tackline,单元测试并不能找到所有问题。 - Ole V.V.

0

Yoda条件是指在变量前面放置文字。

word.equals("s") 读作“word equals s”

"s".equals(word) 人类读作“s equals word”

我们的大脑更容易理解第一个例子,代码也更清晰。

我个人认为使用Yoda条件的唯一原因是防止赋值,例如“if (42 = i)”而不是“if(42 == i)”。


请注意,在Java中if (i = 42)也是不被允许的,因为花括号之间必须是一个布尔表达式而i = 42不是一个布尔表达式。 - Jesper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接