写完上一篇关于else-if
子句的陷阱后,想起来以前还遇到过类似的关于else
子句以及switch
表达式中default
条件的陷阱,本文就记录这个问题,并给出相关解决方案/建议。
问题描述
相信大家在工作中都见到过类似的代码:
if (mediaInfo.getType() == MP3) {
//...处理mp3的情况
} else { //假设此时的type只有mp3和mp4两种值时
//...处理mp4的情况
}
一开始我们的type
只有MP3
和MP4
两种,有时候为了方便,就用if
子句处理MP3
而用else
子句来处理MP4
的情况,这是一个比较糟糕的实践。这样的写法可能会导致意料之外的bug,因为这里的else
子句有以下两个性质:
else
的条件是隐式的,需要根据上下文推导,而不是直接观测;else
的条件是变化的,当新增else-if
子句的时候,else
的条件也会发生变化。
性质一使得else
的条件不是那么直观,这又会放大性质二中的问题。也就是说,当else
的条件发生变化时,它不是可以直接观测到的,容易被忽略,那么就可能因为修改代码时没有注意到条件的改变而导致程序逻辑错误。
比如,上面那段程序,今天增加了MKV
的视频类型,在忘记添加相应的else-if
判断子句的时候就会导致程序正常编译且可以运行,但是可能导致程序的逻辑错误:把MKV
当作MP4
处理。有时候,负责添加MediaInfo
中type
值类型的开发者并不负责这个数据类型的使用,这就导致了问题的发生。
导致上面问题的另一个原因是这段程序中else
子句的条件并不是单一条件,它包含了:
type
为MP4的情况;type
为MP3
和MP4
以外的非法值的情况。
解决方法或建议
上面程序改成下面这样会是比较好的实践:
if (mediaInfo.getType() == MP3) {
//...处理mp3的情况
} else if (mediaInfo.getType() == MP4){
//...处理mp4的情况
} else { //例外情况
//处理非法值的情况,记录日志或抛出异常
}
这样在新增类型值的时候如果忘记添加子句,那么在程序运行的时候就可以根据日志或崩溃记录及时发现问题根源,且程序的表达更加明确。
最后,总结得出以下建议:
else
子句不要包含隐式复合条件,很可能其中就有变化的条件,而这个变化可能无法直接观测到;- 条件分支中正常情况通过
if
和else-if
来处理,而else
只处理例外情况; - 值的枚举处理可以通过swtich表达式或模式模式匹配来完成,if-else仅用于简单条件(比如,isEmpty、isNull),因为这种情况下条件的分支数是固定的(只有是否两个),而else的条件也是固定的(如果if条件中是
isXXX
,那么else子句就是isNotXXX
)