【问题标题】:Else after return from first part of an if statement从 if 语句的第一部分返回后的 else
【发布时间】:2020-09-19 09:26:45
【问题描述】:

库的BigInteger.gcd(...) 方法的实现从这些语句开始:

public BigInteger gcd(BigInteger val) {
    if (val.signum == 0)
        return this.abs();
    else if (this.signum == 0)
        return val.abs();
    ...
}

在这种情况下,else 关键字的用途是什么?它只是程序员忘记删除的旧代码的残余,还是以某种方式影响性能?

我理解,在这种特殊情况下,带有和不带有 else 的版本在语义上是相同的。然而,很多时候,我面临着两种选择

<Some method signature>(...) {
    if (...) {
        <Short branch>
        return ...;
    } else {
        <Long branch>
        return ...;
    }
}

<Some method signature>(...) {
    if (...) {
        <Short branch>
        return ...;
    }
    <Long branch>
    return ...;
}

哪个选项在性能方面更好(注意这个问题是 Java 特有的)?如果两种情况下的性能几乎相同,那么哪一种在可读性方面更好?

【问题讨论】:

  • 库实现没有使用大括号{},所以我不知道我会对他们的特定风格给予多少信任。我也不知道给定代码的年龄。我也不知道任何性能差异(并且 JIT 可能对这两种情况都有优化)。至于可读性,那是主观意见。但我认为if 中的短分支然后没有else 是最易读的。您还可以分解“长分支”以委托给不同的方法。但这只是我个人的看法。
  • @ElliottFrisch 此代码来自 JDK 8 和 JDK 11(给出的部分相同),顶部的版权行指的是 1996, 2018。关于大括号{},他们在上面几行中使用了它们。我个人的看法是,单行块不需要它们,但我认为这无关紧要。
  • 我认为您可以绝对肯定,JIT 优化涵盖了 if/else 用法上的这些细微差别。在可读性方面:我会使用第三个代码 sn-p 用于短方法,使用第二个 sn-p 用于长方法。
  • 这是风格和品味的问题。我本可以发现以gcd() 中看到的方式编写代码很自然,只是我会包含大括号。
  • 为了它的价值,Eclipse 有一个设置来标记甚至禁止 不必要的 'else' 语句,对于不喜欢在 @ 中完成方式的程序员或团队987654334@。它在 Preferences->Java->Compiler->Errors/Warnings->Unnecessary code 下。

标签: java performance if-statement return readability


【解决方案1】:

在我的机器上,使用 java 1.8.0_172,两个版本产生相同的字节码。所以这是在编译时优化的。 JIT 编译器会在两种情况下看到相同的输入,因此运行时性能将是相同的。

其他版本:

public class MyTest {
    public static void main(String[] args) {
        if (args.length > 2) {
                System.out.println("More than 2");
                return;
        } else {
                System.out.println("Hello");
        }
    }
}

没有其他版本:

public class MyTest {
    public static void main(String[] args) {
        if (args.length > 2) {
                System.out.println("More than 2");
                return;
        }
        System.out.println("Hello");
    }
}

然后用java MyTest.java编译它们,然后用javap -c MyTest得到字节码,比较两个来源的字节码。它们是相同的。两者都生成以下字节码:

Compiled from "MyTest.java"
public class MyTest {
  public MyTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: aload_0
       1: arraylength
       2: iconst_2
       3: if_icmple     15
       6: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       9: ldc           #3                  // String More than 2
      11: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      14: return
      15: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      18: ldc           #5                  // String Hello
      20: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      23: return
}

现在为了可读性,这当然是主观的。我更喜欢没有不必要的else的版本。

【讨论】:

    【解决方案2】:

    我的价值 0.02 美元:当面临两种选择时

    <Some method signature>(...) {
        if (...) {
            <Short branch>
            return ...;
        } else {
            <Long branch>
            return ...;
        }
    }
    

    <Some method signature>(...) {
        if (...) {
            <Short branch>
            return ...;
        }
        <Long branch>
        return ...;
    }
    

    我喜欢尽可能选择选项 #3:

    <Some method signature>(...) {
        T result;        
        if (...) {
            <Short branch>
            result = ...;
        } else {
            <Long branch>
            result = ...;
        }
        return result;
    

    对于像这样的简单案例,我喜欢方法的单一逻辑结束(即return 语句)与其物理结束重合。

    更有效率? 疑。

    效率低?可能不是。

    更具可读性? 这么认为,但我不能保证你会这么认为,也不能证明你应该这样。

    把它扔出去。

    【讨论】:

    • 感谢第三版。我更喜欢仅在不可避免的情况下创建局部变量,这就是为什么我在这里错过了这种可能性。理论上,有多个返回点而不是一个返回点应该不会影响性能。
    猜你喜欢
    • 2016-07-23
    • 2021-08-31
    • 1970-01-01
    • 2023-02-21
    • 2020-07-30
    • 2016-10-30
    • 2020-08-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多