5

在无数(好吧,无数,但很多)场合,尤其是在类的方法/函数中,我一直处于想要在 void-return 函数中执行一组操作的情况,但仅if(condition met). 在大多数情况下,我可以看到(假设代码有效)如何通过简单地在块else中返回来完全删除语句。if

这是一个特定的示例,以防万一:

使用 else 语句(老师会如何展示它)

    private void ifThisDoThat(params){
        if(dependenciesNotMet) return;
        else{
            ////DO STUFF HERE...
        }
    }

没有(更简约)

    private void ifThisDoThat(params){
        if(dependenciesNotMet) return;
        //Assuming the above does not execute, DO STUFF HERE...
    }

我在想else,如果根本没有优化,删除该语句将被归类为微优化,但仍然认为我会要求我自己的教化。

最后:

使用 areturn删除else块有什么好处吗?

else如果我使用语句,编译器会做额外的工作吗?

是否有理由总是使用else(在出现错误或其他原因时)?

4

5 回答 5

5

这是错误的优化。编译器实际上可能需要更长的时间来编译,从中间返回,任何优化编译器都会为两种形式生成基本相同的可执行代码。

就风格而言,有时一种风格很好,有时另一种。立即返回风格的危险是:

  1. 如果方法底部有任何常见的完成/清理逻辑,则会被遗漏。在大方法中很容易忘记逻辑是存在的,在大方法中很容易将这样的逻辑编辑成以前没有的方法。这类错误很难找到。
  2. 因为它基本上消除了在方法底部使用“清理”逻辑的选项,它可能导致各个if分支中的逻辑激增,从而产生比标准 if/then/else 更多的混乱。
  3. 它与良好的“结构化编程”实践背道而驰。

也就是说,在某些情况下,中间返回样式是更好的选择:

  1. 有多个顺序if语句的情况,每个语句都有一个简单的主体,每个语句都能够以 return 结尾。
  2. 一个非常简短的方法的案例,其中“快速退出”是自然且相当明显的。
  3. “快速退出”的情况(例如,因为某些数据项为零)非常接近较长方法的顶部。
于 2013-04-01T17:50:21.100 回答
2

一项快速测试可以回答您。想象一下,你有:

public void x(int i){
    if(i == 0){
        System.out.println("zero");
        return;
    }
    System.out.println("not zero");
}

public void y(int i){
    if(i == 0){
        System.out.println("zero");
        return;
    }
    else {
        System.out.println("not zero");
    }
}

如果您查看编译后的代码(使用javap -v <class>):

x的代码:

  public void x(int);
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: iload_1       
         1: ifne          13
         4: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
         7: ldc           #22                 // String zero
         9: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        12: return        
        13: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
        16: ldc           #30                 // String not zero
        18: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        21: return        
      LineNumberTable:
        line 6: 0
        line 7: 4
        line 8: 12
        line 10: 13
        line 11: 21
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      22     0  this   Lpt/kash/Test;
               0      22     1     i   I
      StackMapTable: number_of_entries = 1
           frame_type = 13 /* same */

y的代码:

  public void y(int);
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: iload_1       
         1: ifne          13
         4: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
         7: ldc           #22                 // String zero
         9: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        12: return        
        13: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
        16: ldc           #30                 // String not zero
        18: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        21: return        
      LineNumberTable:
        line 14: 0
        line 15: 4
        line 16: 12
        line 19: 13
        line 21: 21
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      22     0  this   Lpt/kash/Test;
               0      22     1     i   I
      StackMapTable: number_of_entries = 1
           frame_type = 13 /* same */

区别是……没有。编译器足够聪明,可以优化代码。

因此,底线是(如前所述):优化可读性和简单性

于 2013-04-01T17:47:21.947 回答
1

这是风格问题。它不应该影响生成的代码。这不是优化。

就个人而言,如果可能,我总是使用 return,因为我认为它使代码更具可读性——您不必四处寻找 else 语句的结尾。

于 2013-04-01T17:35:26.247 回答
0

执行没有区别,无论如何它都会优化到相同的东西,但是为了可读性我推荐这个(使用一个返回原则):

private void ifThisDoThat(params){
    if(!dependenciesNotMet) { // would be nicer to change this to if(dependenciesNotMet)
        ////DO STUFF HERE...
    }
}
于 2013-04-01T17:49:22.403 回答
0

14: goto 25用 javap 测试程序,除了第二种情况下的 JUMP 指令外,我几乎看不出有什么不同。

如果您在 else 语句上没有要执行的内容,我看不到添加 else 的理由。您可能想使用它来进行日志记录等。

public class Test {
    public static void main(String[] s){
        if(testMethod()){
            System.out.println("in if");
        }
        System.out.println("in else");      
    }   
    static boolean testMethod(){
        return false;
    }
}

导致

    public static void main(java.lang.String[]);
      Code:
       Stack=2, Locals=1, Args_size=1
       0:   invokestatic    #16; //Method testMethod:()Z
       3:   ifeq    14
       6:   getstatic       #20; //Field java/lang/System.out:Ljava/io/PrintStream;
       9:   ldc     #26; //String in if
       11:  invokevirtual   #28; //Method java/io/PrintStream.println:
                            (Ljava/lang/String;)V
       14:  getstatic       #20; //Field java/lang/System.out:Ljava/io/PrintStream;
       17:  ldc     #34; //String in else
       19:  invokevirtual   #28; 
      //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   22:  return

public class Test {
    public static void main(String[] s){
        if(testMethod()){
            System.out.println("in if");
        }else{
            System.out.println("in else");
        }
    }   
    static boolean testMethod(){
        return false;
    }
}

会产生,

public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=1, Args_size=1
   0:   invokestatic    #16; //Method testMethod:()Z
   3:   ifeq    17
   6:   getstatic       #20; //Field java/lang/System.out:Ljava/io/PrintStream;
   9:   ldc     #26; //String in if
   11:  invokevirtual   #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   14:  goto    25
   17:  getstatic       #20; //Field java/lang/System.out:Ljava/io/PrintStream;
   20:  ldc     #34; //String in else
   22:  invokevirtual   #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   25:  return
于 2013-04-01T17:55:18.680 回答