Java:if-return-if-return vs if-return-elseif-return

问一个无关的问题 ,我有这样的代码:

public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; // Check property values } 

我得到了一条评论,声称这不是最优的,相反(如果我理解正确的话)应该这样做:

 public boolean equals(Object obj) { if (this == obj) return true; else if (obj == null) return false; else if (getClass() != obj.getClass()) return false; // Check property values } 

由于返回语句,我无法真正理解为什么它们中的任何一个应该比另一个更高效或更快。 鉴于某个对象,就我所见,这两种方法都必须进行相同数量的检查。 并且由于返回语句,不会在其中任何一个中运行额外的代码。

我在这里错过了什么吗? 有东西吗? 是否有一些编译器优化或正在发生的事情或其他什么?

我知道这是微优化,我很可能坚持第一种方式,因为我觉得它看起来比较清晰,所有的ifs都在同一个位置。 但我无能为力; 我很好奇!

对于这两种情况,生成的字节代码是相同的,因此它纯粹是一种风格问题。

我生成了两个方法e1e2并且都生成了这个字节代码(使用javap -v读取):

 public boolean e1(java.lang.Object);
  码:
    Stack = 2,Locals = 2,Args_size = 2
    0:aload_0
    1:aload_1
    2:if_acmpne 7
    5:iconst_1
    6:ireturn
    7:aload_1
    8:ifnonnull 13
    11:iconst_0
    12:ireturn
    13:aload_0
    14:invokevirtual#25;  //方法java / lang / Object.getClass :()Ljava / lang / Class;
    17:aload_1
    18:invokevirtual#25;  //方法java / lang / Object.getClass :()Ljava / lang / Class;
    21:if_acmpeq 26
    24:iconst_0
    25:ireturn

我省略了我之后编写的代码以使其编译。

两者都没有比另一个更有效率。 编译器可以很容易地看到两者是相同的,事实上,Suns / Oracles javac为这两种方法生成相同的字节码

这是一个IfTest类:

 class IfTest { public boolean eq1(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; return true; } public boolean eq2(Object obj) { if (this == obj) return true; else if (obj == null) return false; else if (getClass() != obj.getClass()) return false; return true; } } 

我用javac编译它,反汇编如下:

 public boolean eq1(java.lang.Object); Code: 0: aload_0 1: aload_1 2: if_acmpne 7 5: iconst_1 6: ireturn 7: aload_1 8: ifnonnull 13 11: iconst_0 12: ireturn 13: aload_0 14: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class; 17: aload_1 18: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class; 21: if_acmpeq 26 24: iconst_0 25: ireturn 26: iconst_1 27: ireturn 

 public boolean eq2(java.lang.Object); Code: 0: aload_0 1: aload_1 2: if_acmpne 7 5: iconst_1 6: ireturn 7: aload_1 8: ifnonnull 13 11: iconst_0 12: ireturn 13: aload_0 14: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class; 17: aload_1 18: invokevirtual #2; //Method Object.getClass:()Ljava/lang/Class; 21: if_acmpeq 26 24: iconst_0 25: ireturn 26: iconst_1 27: ireturn 

也就是说,我建议使用第一个版本(没有else )。 有些人可能认为它其他部分相比更干净,但我认为相反。 包括 else表示程序员没有意识到这是不必要的。

我认为没有任何实际理由可以用任何方向替换其中一个实现。

如果你想在一种方法中避免多个return语句,那么第二个例子就有意义了 – 有些人更喜欢这种编码方式。 然后我们需要if-else if构造:

 public boolean equals(Object obj) { boolean result = true; if (this == obj) result = true; else if (obj == null) result = false; else if (getClass() != obj.getClass()) result = false; return result; } 

这样想吧。 当执行一个return语句时,控制会离开方法,所以else并没有真正添加任何值,除非你想说它增加了可读性(我不认为这样做,但其他人可能不同意)。

所以当你有:

 if (someCondition) return 42; if (anotherCondition) return 43; 

else添加到第二个if没有任何价值。

事实上,我在编写名为Resharper的 C#代码时使用了一个工具,在这些情况下它实际上会将else标记为无用的代码。 所以我认为通常情况下,最好将它们排除在外。 正如Joachim已经提到的那样,编译器无论如何都会优化它们。

我认为这段代码可以改进一点(请注意,它非常易读):

  if (obj == null) return false; if (getClass() != obj.getClass()) return false; 

instanceof运算符相当于两者的组合,可能更快 – 更少的代码,没有方法调用:

  if (!(obj instanceof MyClass)) return false; 

但是我知道什么……我懒得分析字节码(之前从未做过)。 😛