个人觉得这个和Java诞生的背景有关系。
Java语言是一个程序设计工程化的尝试,更少的选择,更标准的编写方式是Java的风格。在这个角度上来说,既然返回值类型是必须声明的,事实上异常也是一种返回值,声明可能返回的异常的类型也很合理。在语言层面上要求程序员对所有返回值(包括异常)进行明确的说明,摒除隐式的rethrow,这个思路是没有问题的。
当然实际运用中我们都知道这是个很失败的语言特性就对了。
简单来说,Checked Exception是个出发点很好,但是并没有什么卵用的设计。
说出发点很好,是因为我们都知道软件会有各种各样的问题,而严谨的处理这些问题会很好的提高软件的健壮性。Checked Exception的观点是让一个方法指定自己一定会抛什么异常,调用者必须决定一定要处理(catch),或者明确声明继续向上抛(throws)。那么整个程序对异常的处理就非常的明确了,程序员也有章可循,UT,测试也能很明确该处理什么错误。
但是,现实是骨感的。如果说比较接近底层的系统还能相对的设计出比较完备严谨的异常体系的,那么业务系统上这个干是严重吃力不讨好的。在业务系统中,一个典型的业务接口,有一个正常的处理结果,但是可能却有几十个不正常的情况发生。而Checked Exception要求必须每层调用,要不挨个处理,要不挨个声明往上抛。
挨个处理是不现实的,因为大部分情况下,大多数异常都是极端情况,平时很少出现,或者说即使出现了,影响也不大。比如,一个基金的详情,底层可能调用十几个不同的基金信息数据源(基本信息,经理,基金公司,费率,净值,重仓股……)。如果因为意外少个一个两个的,最好的办法是“软处理”。如果是必须使用Checked Exception的话,那么很多人都会这么干:
try { Fund fund = getFund(fundCode); } catch (SomeNotImportantButCheckedException e) { // do nothing } 程序员会用IDE自动产生出catch block但就是不处理。或者干脆:
try { Fund fund = getFund(fundCode); } catch (Exception e) { // do nothing } 这比不catch,自动往上抛更差。所以一般来讲,业务系统都会有个收底的错误处理,这个处理可能在业务系统的最高层。大部分不需要认真处理的异常往上抛。当真的意识到某个问题是个值得仔细处理下时,可能才会专门为它仔细的设计Exception和相应的处理。
挨个明确throws也是不现实的。一般我们看throws后边能挂3~4种Exception。按照上面的假设,可能要搞出几十种Exception,而且随着层级的提高这个数量会约堆越多。Java这里一般的建议是用类的体系来组织这些Exception,然后throws一个合适的基类。但我们都知道设计一个比较好的类体系是很困难的事情。更何况大多数异常都是不重要,收底处理的。为不重要的事情花费精力实在是不值当的,于是我们可以看到大家在这时基本上都会简化处理,变成直接用 Exception、 RuntimeException (连throws也不想用)或者自己封装一个“BizException”包装一下错误码和相关信息。
上面这些还都是在设计时可以定义所有异常的情况下遇到的问题。但在业务会剧烈变化的情况下,根本不可能一开始就预见所有可能的问题。强行加Checked Exception对于业务系统的接口来说,是不向前兼容的。如果一旦真的加上了,所有的调用方都会必须逼着跟着改。这种情况对于一个系统内部,一个code base可能还好处理。对于可能跨系统的调用(比如你写了一个java lib,别人用maven dependencies依赖),就可能是灾难性的。如果有某些组件因为种种原因无法升级,就不能使用新的代码。
因此,一个好的错误处理体系,最好能满足以下要求:
基于此,我在Team中对Java的异常是这么要求的:
相信大多数团队也会这么做。
说完了Java,我们可以看看其他语言都是怎么处理异常的。比如go用了err(大致等价于错误码,但可以包含一些数据信息),因此异常可以不捕获往上抛的好处就得不到。因此这可以解释为什么很多搞底层系统开发的人并不会觉得go没Exception很难用,因为反正错误都需要严谨的处理,Exception那点优势可能不是很重要。但是即便如此,go并不会强制调用者必须处理写if err != nil 。
BTW,给go加上Exception的呼声似乎越来越强烈,估计是业务开发者越来越多了。
javascript也有Error,但是不是很喜欢搞继承体系。因此javascript里一般就只会用“Error”,“TypeError”这种简单的东西。因为动态语言,开发者可以选择自己往Error里赛自己喜欢的东西,并用一些松散的约定来解决问题。比如:
throw Error("ERR_INVALID_PASSWORD"); 简单的用字符串来定义错误。javascript的主要应用场景在前端,这时出错要不给用户展示错误信息,要不用错误上报接口报给后端。复杂的异常体系基本没用,更谈不上什么Checked exception。
nodejs这边的exception处理可以借鉴很多java这边的经验。
kotlin直接砍掉了Checked Exception,但保留了其他常规的异常语法(改成了Expression,所以用起来爽很多)。他的文档直提供了两个论据:
swift更有趣,swift认为函数的异常模式有两种:
编译器会强制确保这个语义的正确。throws这种方式,大概等价于Java中直接throws Exception。
因此从工程角度和语言发展角度来讲,Checked Exception早已经被扔进了垃圾堆。在整个工程项目的错误处理体系里,它的作用已经越来越少。新的语言纷纷抛弃掉这个华而不实的设定。因此我也希望广大初学者只要知道Checked Exception是什么就好,实战时还是根据业务场景来设计错误处理。
--------
P.S. 也许还有人觉得Checked Exception是一种可以推进减少程序错误,提高健壮性的好措施。错的是懒惰的,不称职的程序员,而不是Checked Exception。但从我认为,如果一个措施不能有助于解决问题,反而加重问题,那就是无用的。不要把时间和精力浪费在无用的事物上。
本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度,google,bing,sogou 等
© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有