【问题标题】:Semicolon at end of 'if' statement'if' 语句末尾的分号
【发布时间】:2012-12-16 06:10:34
【问题描述】:

今天找了半个小时的bug,发现可以在if语句后面加分号代替代码,像这样:

if(a == b);
// Do stuff

这基本上意味着无论a 是否等于b,这些东西都会完成,而if 语句没有任何意义。为什么Java不给我一个错误?在什么情况下这会有用?

【问题讨论】:

  • 看起来像无操作,即如果 a 与 b 相同,则什么也不做;你可以在那里添加一个 javadoc,但我想不出任何其他用途
  • 不,这没有任何用处。为什么?如果我在 Java 中不懂的每一件事都能得到一毛钱:)
  • 一般规则是分号(;) 完成语句。所以在那之后什么都没有发生if
  • 啊哈!这是 Python 语法的一种错误 - 视觉缩进显示块范围,这使得这种错误在 Python 中不太可能发生,因为通常情况下,如果代码块没有缩进到 if 语句中, 那么它不在 if 语句中。
  • @gefei : 这没有什么用处,是的。但在某些情况下,这会(或被用来)影响程序。请看我的回答。

标签: java if-statement


【解决方案1】:

为什么会这样?

Java Language Specification 说:

空洞的陈述

空语句什么都不做。

EmptyStatement:
    ;

空语句的执行总是正常完成

它本质上意味着你要执行空语句 if a==b

if(a == b);

你应该怎么做:

这个问题有两种主要的解决方案:

  1. 您可以通过使用代码格式化程序来避免空语句的问题 以及if{} 内的周围的东西。通过做这个 您的空语句将更具可读性。

    if(a == b){
      ;
    }
    
  2. 您还可以检查用于静态代码分析的工具,例如:

    他们可以立即突出显示此类问题。

我建议将这两种解决方案结合起来。

【讨论】:

  • 即使是最简单的解决方案,使用代码格式化程序,也能解决这个问题。打算在 if 中的语句将出现在与 if 相同的缩进处。
  • 当然,但我建议同时使用格式化程序和静态代码分析。
  • 你不能通过使用大括号来避免这种情况,因为在右括号之后和左大括号之前的意外分号是一个语法上有效的程序......而且可能不是你想要的。不过,我同意物质代码可能有助于避免这个问题。
【解决方案2】:

我想不出它有什么用处。它对于像

这样的循环很有用
 while(do something);

 for(init; do something; something else);

如果您经常在 IDE 中使用代码格式,这些错误就会变得很明显。一些 IDE 也将此突出显示为可能的错误。

【讨论】:

    【解决方案3】:

    这在什么情况下有用吗?

    有用吗?就像“让你的代码更干净、更清晰、更快、更易于维护”一样?一点也不。这很可能是糟糕、混乱的代码

    但不一定是良性的。这样的语句可以执行操作和/或由于方法引起副作用而改变状态,并且由于short-circuiting of operators 而可选地评估这些方法。

    if( a() && b() );
    

    这里,a()b() 可能会做一些事情,而b() 只会在a() 为真时执行。

    至于为什么,我认为答案很简单,即偏离已定义的预期行为(例如,while(reader.read()); 之类的语句)比开发人员编写糟糕代码的替代方案更糟糕。

    总是有可能写出糟糕的代码。重申一下,这几乎在任何情况下都是糟糕的代码。

    【讨论】:

      【解决方案4】:
      if(a==b)
          println("a equals b");
      

      如果只有一行要执行,您可以使用不带{} 的 IF 语句,因此使用if(a==b); 您是说如果它们相等,则执行和清空语句......所以它什么也不做,然后返回到您的正常循环,在 IF 块之外。

      【讨论】:

      • println("a now equals b");
      【解决方案5】:

      如果使用if 语句,如果条件为真,将执行if 之后的第一条语句。如果您在if 之后有一个块(带有花括号),则它对整个块都有效。如果没有块,它只计算一个语句。单个分号是一个空语句。你也可以像这样从你的例子中编写代码:

      if(a==b) {
          ;
      }
      

      【讨论】:

        【解决方案6】:

        我同意你的观点,这对人类没有任何用处。我怀疑它的存在是因为它简化了语言定义;例如,这意味着if 之后的内容与while 之后的内容相同。

        【讨论】:

          【解决方案7】:

          一个可能的用例:

          if (a==b);
          else {
            // Do something
          }
          

          不好,但可能。

          不过,我确实认为 Java 规范应该不允许空的 if

          【讨论】:

          • 这样的代码实际上可以通过考虑所有情况来防止逻辑错误。 “如果为真,什么都不做,否则……”可以更具可读性,因为您不会被否定错误和“遗漏案例”弄糊涂。如今,大多数编译器都知道这种结构,并且无论如何都会为您反转以提高速度。例如Mercury 语言强制执行此操作!
          • @tudor 我会说即使对于那个用例,最好还是使用if (a==b) { // do nothing } else { doSomething(); },因为更明显的是无操作是故意的,而不是错字。
          • @yshavit 是的。很多时候,评论有助于让事情变得更清晰,但这与这个结构不同。此外, if (a==b); 之间没有功能差异; //do nothing and if (a==b) { /*do nothing*/ } 两者都将通过删除注释和空大小写并反转条件来优化。
          • @tudor 对于编译器,是的,对此毫无疑问。但是让我这样说吧:如果我看到有人按照我的方式做,我会想“这个人一定认为这样更容易表达条件”。如果我看到它们使用分号+else-braces 语法,我会检查 VCS 日志以确保它不是其他人后来添加 else 子句的错字/错误。
          【解决方案8】:

          这是从有更多语法糖来区分表达式和语句的时代遗留下来的。

          基本上,逗号用作列表项分隔符,因此分号用作“语句列表”分隔符。缺点是处理列表中的空项和块中的空语句。

          在项目列表中,Java 使用显式关键字null,但“空语句”只是一个空行。允许空行的存在是继承自 C 的传统的保留。

          为什么要这样做?尤其是当您知道没有执行任何语句时使用if 语句:因为某些 if 语句有副作用:

           int c;
           if ((c = in.read()) != -1);
          

          是的,这不是最好的例子,但基本上它说从流中读取一个字节并且什么都不做。在某些极端情况下可能有用,但即使这个例子不是最好的,它也说明了意图。我们希望在不意外执行任何语句的情况下感受表达式的副作用。

          【讨论】:

            【解决方案9】:

            Java 允许在允许语句块的任何地方使用空块。我确信将此作为所有块的通用规则可以简化编译器。

            我同意这主要是导致难以找到的错误的主要原因。我总是在块周围使用大括号,即使只有一条语句,但 Java 允许您在任何时候使用大括号创建一个块,因此使用大括号无法使您摆脱这种命运。例如,我曾经浪费了 4 个小时试图找到这样的东西:

            while (condition);
            {
                statement;
                statement;
            }
            

            第一行末尾的分号是一个错字,不小心使 while 循环的语句块为空。因为语法是有效的程序编译和运行良好,只是不是我想要的方式。 真的很难找到。

            我可以想到一种情况,非常好允许你有空块,这是这样的:

            if (condition1) {
                do_action_1();
            }
            else if (condition2) {
                //nothing really to do in this case
            }
            else if (condition3) {
                do_action2();
            }
            else {
                do_action3();
            }
            

            在上面的示例中,您希望能够分离出各种条件。请记住,这些条件可能会重叠,因此并不总是可以重新排列顺序。如果其中一个条件真的不需要做任何事情,那么 Java 允许你有一个空块是很好的。否则,当您真的不想做任何事情时,该语言将需要某种形式的“noop”方法来使用。

            我个人更喜欢明确的“noop”语句——但这不是 Java 的定义方式。

            【讨论】:

              【解决方案10】:

              分号结尾,
              如果(a==b);只需在单行中完成语句,这意味着忽略条件的结果并从下一行继续执行
              这段代码很有用,另一方面有时会在程序中引入错误,例如,

              案例1.

              a = 5;
              b = 3;
              如果(a == b);
              prinf("a 和 b 相等");

              案例 2.

              a = 5;
              b = 5;
              如果(a == b);
              prinf("a 和 b 相等");
              将在屏幕上打印相同的输出...

              【讨论】:

              • “这段代码很有用”
              • 是的,在 if-elseif 层次结构中,如果我们需要忽略某些条件的结果,则可以使用它。例如
              【解决方案11】:

              为什么?这是因为它对编译器编写者来说更容易。您不必特殊情况来检查 if(cond) 之后的分号,并且还有一个额外的用法是允许

              if (cond && maybeFunc())
                  ;// Code here I want to ignore
              

              即使允许这样做实际上是一个糟糕的主意。允许然后添加一个案例来检查这一点更容易。

              【讨论】:

              • +1,但不需要使用示例。正如您所展示的,它可以使用,但它远非组织该代码的最佳方式。它很丑陋,但没有理由为了拒绝而对其进行特殊处理——至少从编译器的 POV 来看。从一个 stle POV...
              【解决方案12】:

              我可以想到一个需要空语句的场景(不是if 条件,而是while 循环)。

              当程序只需要用户的明确确认以继续时。当用户确认后的工作取决于其他一些事情并且用户想要控制何时继续时,这可能是必需的。

                  System.out.println("Enter Y to proceed. Waiting...");
                  System.out.println("");
              
                  while(!(new Scanner(System.in).next().equalsIgnoreCase("Y")));
              
                  System.out.println("Proceeding...");
                  // do the work here
              

              【讨论】:

                【解决方案13】:

                只是关于可用性的仅供参考,如果有这样的声明,它会产生什么影响或可以产生什么影响

                考虑如下一段代码。

                int a = 10;
                if ((a = 50) == 50);
                
                System.out.println("Value of a = " + a);
                

                很明显,在这种情况下,if 语句确实会改变输出。因此,这样的声明可以有所作为。

                在这种情况下,这可能是有用的,或者说对程序有影响。

                【讨论】:

                  【解决方案14】:

                  如果你使用的是 Eclipse,你可以让它警告你这些语句:

                  【讨论】:

                    【解决方案15】:

                    jls 中的一些定义解释了这一点(第 14 章):

                    块是语句

                    here 所述,BlockStatementWithoutTrailingSubstatement,而 StatementNoShortIf 又是 Statement。因此,无论何时需要这些,我们都可以插入Block

                    if 子句

                    虽然forwhile-loops 也是如此,但我将使用if-statements。这些规则几乎相同。 if-statements 的语法描述可以在here 找到。

                    IfThenStatement:
                        if ( Expression ) Statement
                    
                    IfThenElseStatement:
                        if ( Expression ) StatementNoShortIf else Statement
                    
                    IfThenElseStatementNoShortIf:
                        if ( Expression ) StatementNoShortIf else StatementNoShortIf
                    

                    所以我们可以在这里使用我们的块。

                    但为什么它与 ; ?

                    ; 定义为EmptyStatement (link),它也是StatementNoShortIf。因此,在有条件的代码片段中,例如if-statement 和循环,如果需要StatementNoShortIfStatement,我们可以将Block 替换为EmptyStatement

                    因此if(Expression)EmptyStatement 有效。

                    为什么不报错?

                    很简单:如果 java 发现无效的语法,它会报错。但是if(Expression)EmptyStatement 是完全有效的语法。相反,javac 如果使用正确的参数启动,则会发出警告。为此,full list of warnings that can be dis-/enabled 列出了警告名称empty。所以用-Xlint:all-Xlint:empty 编译会产生一个警告。

                    您的 IDE 也应该有一个选项来启用这种警告。 对于日食,请参阅@nullptr's answer。在 IntelliJ 中,您可以按 Ctrl + Shift + A,在搜索字段中输入 empty body 并启用警告(在图像中标记)

                    这到底是干什么用的?

                    老实说,从简约的角度来看,它并没有多大用处。通常有一种方法可以在没有“什么都不做”命令的情况下完成任务。个人喜好的问题,你是否更愿意使用

                    if( a() && b() );
                    

                    if( a() ) b();
                    

                    同样适用于使用EmptyStatement 的其他情况。关于这个主题要考虑的一个重要点是代码的可读性。在某些情况下,通过使用 no-op,代码变得更具可读性。另一方面,在某些情况下,使用EmptyStatement 会使代码变得非常难以理解——上面的例子将被计入后来的 IMO。

                    【讨论】:

                      【解决方案16】:

                      在为班级进行编程作业时,我正在使用 N×N 网格的小玩意儿,并将随机小玩意儿的特征与上方、下方、左方和右方的特征进行比较,我发现一个很好的用途是防止嵌套语句和潜在的边界异常。我的目标是最小化代码并避免嵌套 if 语句。

                      if (row == 0); 
                      else (method (grid[row][col], grid[row-1][col]));
                      if (row == N-1);
                      else (method (grid[row][col], grid[row+1][col]));
                      if (col == 0);
                      else (method (grid[row][col], grid[row][col-1]));
                      if (col == N-1);<br>
                      else (method (grid[row][col], grid[row][col+1]));
                      

                      method(Doodad a, Doodad b) 在 a 和 b 之间进行一些操作。

                      或者,您可以使用异常处理来避免这种语法,但它适用于我的应用程序。

                      【讨论】:

                      • if(row != 0) method (grid[row][col], grid[row-1][col]); 在不使用空的if 语句的情况下会产生相同的效果。
                      【解决方案17】:

                      看这个:

                      int a,b,c = 0;
                      if(a == b){
                         c =1;
                      } 
                      System.out.print(c);//1
                      

                      所以,你可以这样写:

                      if (a == b)c=1;
                      

                      但是,如果这段代码是这样的:

                      int a,b,c=0;
                      if (a != b){
                      }
                      if (a == b ){
                        c =1;
                      }
                      

                      你可以这样写:

                      if(a != b);
                      if(a == b )c=1;
                      

                      所以,你会知道if(a != b);做笔记

                      【讨论】:

                        【解决方案18】:

                        if 中的分号表示 if 条件的终止,如 java 中的 ; 被视为语句的结尾,因此执行 if 之后的语句。

                        【讨论】:

                          猜你喜欢
                          • 2019-04-18
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2023-03-28
                          相关资源
                          最近更新 更多