if和if-else有明显的区别吗?

8

给定以下代码片段,是否有明显的区别?

public boolean foo(int input) {
   if(input > 10) {
       doStuff();
       return true;
   }
   if(input == 0) {
       doOtherStuff();
       return true;
   }

   return false;
}

对比。

public boolean foo(int input) {
   if(input > 10) {
      doStuff();
      return true;
   } else if(input == 0) {
      doOtherStuff();
      return true;
   } else {
      return false;
   }
}

这段代码是否应该遵循单一出口原则呢?...
public boolean foo(int input) {
   boolean toBeReturned = false;
   if(input > 10) {
      doStuff();
      toBeReturned = true;
   } else if(input == 0) {
      doOtherStuff();
      toBeReturned = true;
   }

   return toBeReturned;
}

这三种写法在性能上有明显的差异吗?你觉得哪一种更易于维护和阅读?


4
谁发明了那个°&$§?*!单一出口原则?它真的会使代码更易读吗? - whiskeysierra
许多人(包括我)会认为是的,它确实如此。 - Petruza
在这种情况下,#1和#2的功能相同(else是多余的)。 但是,如果这些表达式都可能为真(即:input>50 ==(input%2)),那么情况就完全不同了。考虑您正在测试的条件的范围以及它在未来更新中可能会发生的变化。至于单一出口原则,我看过它使一些代码更易读,而使其他代码更难读,因此将其视为工具而非戒律。 - NVRAM
9个回答

9
使用第二个示例可以非常清楚地说明两个条件是互斥的。
但对于第一个示例,不是那么清晰,在两个if之间添加一个对input的赋值(虽然不太可能)会改变逻辑。
假设将来有人在第二个if之前添加 input = 0
当然这种情况不太可能发生,但如果我们谈论可维护性,if-else就清楚地表示存在互斥条件,而一堆if则没有表达得那么清楚,并且它们相互依存程度不如if-else块。
编辑:现在看来,在这个特定的示例中,返回子句强制执行相互排斥,但是再次强调,我们要考虑可维护性和可读性。
无论如何,在Java中编写代码时,不必担心几个if块的性能问题,如果是嵌入式C在速度非常慢的硬件上运行,或许需要考虑性能问题,但在Java中不需要。

4

使用最适合描述您意图的表单。

如果事情如此简单,不要遵循单一退出原则--这只会让事情更加混乱。


4
  • 首先:

    总有人在你不注意的时候,出于某种奇怪的原因,会添加一些语句,导致这个方法在某些奇怪的情况下失败,每个人(或者更糟糕的是,只有一个人)将花费4小时查看源代码和调试应用程序,最终找到了问题所在。

  • 第二种方法肯定更好,不仅可以防止这种情况发生,而且还有助于明确表明,它是这个那个,以及其他内容。

    如果我们编写的所有代码都在一个if中,长度最多为10行,那么这并不重要,但不幸的是,情况并非如此,还存在其他程序员,他们出于某种原因认为if体应该大于200行...无论如何。

  • 我不喜欢第三种方法,它迫使我寻找返回变量,而找到return关键字更容易。

关于速度性能,它们(几乎)相同。不用担心。


1
在你最后的例子中,不要这样做:
public boolean foo(int input) {
   boolean toBeReturned = false;
   if(input > 10) {
      doStuff();
      toBeReturned = true;
   } else if(input == 0) {
      doOtherStuff();
      toBeReturned = true;
   }

   return toBeReturned;
}

但是这个(请注意使用Java的final):

public boolean foo(int input) {
   final boolean toBeReturned;    // no init here
   if(input > 10) {
      doStuff();
      toBeReturned = true;
   } else if(input == 0) {
      doOtherStuff();
      toBeReturned = true;
   } else {
      toBeReturned = false;
   }
   return toBeReturned;
}

通过这样做,您可以清晰地表达您的意图,这对于支持“编程意图”的IDE来说是一件大好事(无需“编译”即可查看潜在错误,即使是部分AST,良好的IDE也可以实时检查不完整的源代码并立即发出警告)。
这样,您就可以确保不会忘记初始化返回值。如果以后您决定需要另一个条件,这将非常有用。
我经常这样做,自从我开始使用IntelliJ IDEA(大约是4版本,很久以前)以来,这为我节省了许多愚蠢的注意力错误...
有些人会认为这对于如此简单的情况来说是太多的代码,但这完全错过了重点:重点是使意图清晰,以便代码易于阅读,并且稍后可以轻松扩展,而不会意外忘记分配toBeReturned并意外忘记从稍后添加的子句中返回。
否则,如果“简洁性”是游戏的名字,那么我会写:
public boolean foo(int a) {
  return a > 10 ? doStuff() : a == 0 ? doOtherStuff() : false; 
}

无论是doStuff还是doOtherStuff都将返回true。


0

当这两个例子不相似时,请考虑这种情况:

    public boolean foo(int input) {
        if (input > 10) {
            // doStuff();
            return true;
        }
        System.out.println("do some other intermediary stuff");
        if (input == 0) {
            // doOtherStuff();
            return true;
        }

        return false;
    }

对比

    public boolean foo(int input) {
        if (input > 10) {
            // doStuff();
            return true;
        } 
        //System.out.println("doing some intermediary stuff... doesn't work");
        else if (input == 0) {
            // doOtherStuff();
            return true;
        } else {
            return false;
        }
        return false;
    }

第一种方法可能更加灵活,但两种公式在不同情况下都有其用途。

关于性能,我认为对于任何由理智的程序员编写的常规Java应用程序来说,差异太小而无需考虑 :)


0

在您的情况下,第二个if只有在第一个if失败时才会被调用,因此在这里它不太重要,但是如果您的第一个if执行了某些操作并且没有返回,则第二个if(然后始终为false)仍将被测试,除非它在else-if中。

换句话说,if-else-if和if-if之间的区别在某些情况下很重要,但这不是其中之一。

例如:尝试这样做,然后尝试删除else。您将获得两个不同的输出:

int someNumber = 1;
if(someNumber < 5)
{
    someNumber += 5;
    Console.WriteLine("First call.");
}
else if(someNumber >= 5)
{
    Console.WriteLine("Second call.");
}

0
语义上来说,不是的。性能取决于编译器是否能够判断这两个条件不可能同时为真。我敢打赌标准的Sun编译器可以做到。是否使用单一出口原则取决于个人喜好。我个人非常讨厌它。

0

版本 #1 和 #2 可能比 #3 更快,但我认为性能差异很小。我宁愿关注 可读性

个人而言,我永远不会使用版本 #2。在 #1 和 #3 之间,我会选择针对特定情况产生最易读代码的那一个。我不喜欢我的方法中有太多的退出点,因为这使得代码难以分析。然而,在某些特殊情况下,如果我们立即退出一些特殊情况并继续处理主要情况,流程会变得更加清晰。


-1
第一段和第二段代码片段之间实际上没有区别。然而,第三段代码片段非常低效。因为你要等到方法中的最后一行代码才将程序的控制权返回给调用者,这样就浪费了处理能力/内存,而前两个代码片段会在它确定一个条件为真时立即返回控制权。

3
你的观点几乎可以确定是错误的 - 任何现代编译器都会将它们优化为相同的代码。而且,无论如何,“将控制权返回给程序”都不需要占用处理能力或内存空间。 - KevinDTimm
也许你误解了。我指的是如果第一个条件验证为真,就会浪费处理能力。在他的前两个示例中,控制流将返回到调用方法,而不是继续进行无意义的验证。所以请纠正我,但说执行额外的不必要代码“不需要处理能力”是不正确的,对吗? - KSwift87
不对。从语义上讲,你的句子是错误的。“第三个代码片段非常低效,因为你要等到代码的最后一行才返回结果,这浪费了处理能力。”你只批评了第三个代码片段,而已经忽略了1和2。我的观点依然成立。 - KevinDTimm
重新阅读了我的回答后,你是对的,感谢我的语义学。我没有彻底解释为什么我最初认为代码片段3可能会浪费处理时间/功率。在重新阅读OP之后,我也意识到我要提出的关于代码片段3的观点将因代码的“else”部分而无效。无论如何,在重新阅读OP时,我至少注意到了“toBeReturned”变量。该变量(除了在某种程度上帮助我挽回面子)创建了更多的开销(占用更多内存),使其效率低下。所以,对于OP,如果可以避免,请不要编写像代码片段3这样的代码块。 :) - KSwift87

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