何时在Java中使用switch语句
我很欣赏任何可以通过switch语句完成的事情都可以通过if else语句来完成。
但是,当有人应该使用开关而不是if else的声明时,是否有风格规则。
嗯,在我看来,在许多情况下, switch
感觉比在if
/ else if
梯子上“更轻”。 基本上,您的代码方式中没有大括号和括号的语法。 话虽这么说, switch
inheritance了C的语法。 这意味着除非你引入新的块,否则你有变量而且只有一个变量范围。
仍然,编译器能够将switch
语句优化为查找表,并在处理枚举时对文字执行编译时检查。 所以,我建议如果你正在处理数字或枚举类型,通常最好使用switch
if
/ else if
。
Switch在清晰度方面具有一个优势:
switch (i) { case 1: // do something break; case 2: case 4: // do something break; case 5: // do something break; }
如果2和4的代码相同,则可能比以下更清楚:
if ( i == 1 ) { // do something } if ( (i == 2) || (i == 4) ) { // do something } if ( (i == 5 ) { // do something }
您(或其他程序员)也可以更容易地分割出2个和4个案例。
我使用switch语句作为枚举,如果else if if else语句,它更具可读性。 但是,您应该尽量避免在OO设计中进行此类检查。
当您打开基元/枚举/包装类型的不同值时,可以使用switch语句。 (并非所有原始/包装类型,只有受支持的类型 – byte,short,char,int)。
如果/ else处理其余的。
例如,说起来更美观:
int i = getValueOfI(); switch (i) { case 1: // do something break; case 2: // do something break;
等等
比
if (i == 1) { } else if (i == 2) { }...
对于大量的案件。 但是你不能打开字符串,函数值或复杂的条件,所以如果你需要做任何这些,那么你就会被if / else所困扰。
在个人情况下,我用来发现这两种结构有点过于程序化。 虽然它可能被视为OO extermism,但我使用包含if的每个case的内部接口实例的Map。 我认为它允许更好的代码隔离。 但是,为了回答你的问题,当我遇到真正重叠的情况时,我只使用开关(我不使用break语句)。 不幸的是,它实际上不是一个可维护的代码块。
我建议简单的规则:
当您有至少2个选项来区分,当数据类型可用于交换机以及所有选项具有常量值时,请始终使用switch
。
有三个很好的理由。 一,在大多数情况下, switch
比if
/ else
级联更快。 二,它使代码的意图更清晰。 三,哦,如此糟糕的被遗忘的break
困境是一个较小的邪恶,而不是巨大的if
/ else
级联,因为有人忘记了else
而意外破坏。
Java VM实际上支持两种不同类型的交换机:tableswitch和lookupswitch指令。 如果所有case
常量都在一个窄范围内,则tableswitch由编译器生成,否则它会生成一个lookupswitch。 对于具有许多情况的大型switch
语句,tableswitch比lookupswitch更有效。 lookupswitch通常通过某种forms的二进制搜索来实现。
对我来说有两个因素:
可读性以及是否要使用值或条件范围来决定某些内容(在switch语句中,您只能使用单个整数或枚举值)。
首先,switch语句必须是可用的。 如果开关基于变量的值,则可以使用它。 如果它基于一个复杂的AND / OR / NOT布尔表达式,它对每个条件都有所不同,那么根本就不能使用它。
话虽如此,如果它适用,并且至少有2个案例,那么我使用开关。 它更容易扩展,更易于阅读和检查。
切换和枚举。
如果您正在测试的枚举值因任何原因合法地为null
,则将其置于switch语句中将生成NullPointerException。 如果不看字节代码,它会有点莫名其妙。
解释:枚举是1.5中引入的语法糖。 Switch语句仍适用于good-ole int,但它使用的值是分配给enum的序数。 为了得到序数,枚举值必须是非空的。
另一方面, if statement
很乐意接受枚举值的null
,并且在没有NPE的情况下测试失败。
也许有点offtopic,但如果我回答标题中的问题,那么我会说你不应该在所有情况下使用switch,其中case表示某个对象的状态。 在这些情况下,状态模式是更漂亮的解决方案。
我总是发现java switch语句没有我想要的那么强大。 在他的最后一个版本中, lambdaj通过巧妙地使用闭包和Hamcrest匹配器来实现它 。
例如, lambdaj Switcher允许实现策略模式。 假设您必须根据要排序的列表的某些特征在三种排序算法之间切换。 特别假设我们有一个专门用于字符串的算法:
public List sortStrings(List list) { // a sort algorithm suitable for Strings }
另一个适用于不超过100个项目的小型列表:
public List sortSmallList(List list) { // a sort algorithm suitable for no more than 100 items }
更通用的一个:
public List sort(List list) { // a generic sort algorithm }
给定这3种排序方法,可以创建一种策略,以下面的声明方式选择最合适的策略:
Switcher> sortStrategy = new Switcher>() .addCase(having(on(List.class).get(0), instanceOf(String.class))), new Closure() {{ of(this).sortStrings(var(List.class)); }}) .addCase(having(on(List.class).size(), lessThan(100))), new Closure() {{ of(this).sortSmallList(var(List.class)); }}) .setDefault(new Closure() {{ of(this).sort(var(List.class)); }});
并通过调用Switcher使用最佳可用算法对列表进行排序:
List sortedList = sortStrategy.exec(list, list);
答案取决于您正在做什么以及选择的分布。
如果一个条件占优势,那么if / then是合适的。
if (i == 1){ //do something }else if (i == 2){ // do something else }
如果条件均匀分布,则编译器中的优化将提供性能优势。 随着可能选择的数量增加,这种性能差异变得更加明显。
switch (i) { case 1: // do something break; case 2: // do something else break; .... case N: // do yet something else break; }
也就是说,如果性能不重要,那么你最喜欢的是你最喜欢的(可维护和最容易编写)。
另一方面,如果您的代码位于性能非常重要的热点,那么您应该使用交换机。
对于“非常大”的条件,Mario的lambdaj切换器的例子非常酷,并且在初始化时要小心,这将导致非常高的性能。 它与优化器生成的代码非常相似。 我会定义“非常大”,因为当选项的数量很大或足够复杂以使其值得输入时,并且当后续开发人员试图通过代码时值得支持混淆。 (注释你的代码,为什么你拥有这一切!)。
如果你有太多的条件来检查类似的类型,你可以去切换。
与其他语言(如C或C ++)一样,如果要将给定变量与可能值列表进行比较并根据这些值执行操作,则switch语句非常有用。 它比if else语句更为简洁。
我同意x4u的回答 。
此外还有另一个未提及的实例,但是,我认为最好使用if
– else
块而不是switch
:当使用if
块测试每个case
块中的附加条件时。 我在现有代码中一直看到这种混合物。
例如,我刚刚看到这个代码有一个字符串type
的switch
,然后在两个case
语句中使用if
检查第二个字符串extension
。
public abstract class Temp { boolean valid; public Temp() { String type = getType(); String extension = getFilenameExtension(); switch(type) { case "Image File": { if(!".jpg".equals(extension) && !".png".equals(extension)) { warnWrongImageFormat(); valid = false; } break; } case "Zip File": { if(!".zip".equals(extension)) { warnWrongZipFormat(); valid = false; } break; } default: { valid = true; break; } } } abstract String getType(); abstract String getFilenameExtension(); abstract void warnWrongImageFormat(); abstract void warnWrongZipFormat(); }
相反, if
else
这样做的if
将其减少为一个更清洁,更简单
public abstract class Temp { boolean valid; public Temp() { String type = getType(); String extension = getFilenameExtension(); valid = true; if("Image File".equals(type) && !".jpg".equals(extension) && !".png".equals(extension)) { warnWrongImageFormat(); valid = false; } else if("Zip File".equals(type) && !".zip".equals(extension)) { warnWrongZipFormat(); valid = false; } } abstract String getType(); abstract String getFilenameExtension(); abstract void warnWrongImageFormat(); abstract void warnWrongZipFormat(); }
Switch有两个相关的缺点:
- 它仅限于原始类型和枚举
- 你必须记住“rest”,这可能导致不那么明显的错误
通常,切换是OO设计不佳的标志,因为您最好使用多态。
切换的唯一可能优点是,它更具可读性。 但是
switch (i) { case 1: // do something break; case 2: // do something break; }
比这更具可读性:
if (i == 1) //do something else if (i == 2) // do something else
我会说:不! 你不会有开关的缺点。
我的建议:尽量避免转换。