Java 中的异常要区分受检查异常和运行时异常,我认为这个机制简直是个败笔?

2019-05-27 10:58:23 +08:00
 qwerthhusn

受检查的异常需要捕获或者catch吞掉(一般做法是重新包装一个RuntimeException抛出去),在重载一些接口或类的方法时可能必须要catch(包括 lambda 表达式)

  1. 其他语言应该基本上没有这样做的
  2. 从语义上,其实 Checked Exception 和 Runtime Exception 不好定界,导致不同的框架或第三方库有不同的偏好。举个栗子,Spring 系基本上不会用受检查异常,Jackson 基本上所有 API 都声明了 IOException。
  3. 虽然 Checked Exception 会告诉你需要处理这种异常,但是 Runtime Exception 在有些时候你也需要处理,这个就变成了隐藏的雷。而其实更多时候,Checked Exception 其实也不需要做过多的处理,所以代码中有很多catch (EXception ex) { throw new XXXRuntimeException(ex); }
5104 次点击
所在节点    Java
29 条回复
xuanbg
2019-05-27 11:07:12 +08:00
Checked Exception 就是个沙雕概念!我都知道要怎么处理异常了,还不把检查做在前面,非得抛异常来处理?简直沙雕。。。
GM
2019-05-27 11:34:58 +08:00
首先,这是个好功能。

其次,Runtime Exception 不需要 catch,出错了就让他出错,在最外层 catch 住,返回出错信息,并清理现场就好了。

最后,工作原因,用了一段时间 C#,发现缺了这个功能,用起来非常难受,调用任何一个第三方函数,你都无法知道到底会不会抛异常、会抛什么异常,所以无法使用异常类型来区分不同的情况(除非它的文档有说明并且你仔细阅读了),所以唯一能做的就是 catch (EXception ex) ,非常恶心。
specita
2019-05-27 11:42:26 +08:00
这要看使用者对异常的看法是如何的,至少我觉得并不鸡肋
mooncakejs
2019-05-27 11:48:19 +08:00
checked 异常是个好东西,但是问题出在,不同的第三方 lib 对 checked exception 的理解不一样,给使用者造成了困扰,
szq8014
2019-05-27 11:49:21 +08:00
目前来说倒说不是特别烦 CE, 在 web 开发中我都是声明个 CE 来传异常信息的,比如 用户登录,
User login(String account, String passwd) throws XXXBaseException,成功了返回值就是 User,不成功 Exception 里面的 message 就是失败信息。。

问题就在于许多基础模块里面用了 CheckException 导致一些无关紧要的 try catch 影响代码流畅度。应该和你说的 请“ Checked Exception 和 Runtime Exception 不好定界” 感受差不多?

嗯,个人感觉 CE 是个挺好的东西,就是一开始底层使用过度,导致上层代码都被迫套 try catch 影响心情
mandy0119
2019-05-27 11:51:00 +08:00
我能说你和一楼都不理解异常么。
Checked Exception 是代码知道可能会出某个错误,抛出来让 上一级处理 。
例如我写一个工具类,你传进来的参数有个参数不对,我 catch 到了我一个工具类需要怎么处理呢?我处理了你还是没传这个参数,如果我没有默认的参数,那我就要往上抛一个异常告诉你你用错了,外层调用的人捕捉到这个异常知道自己错在哪,至于你是打特殊的日志还是重新搞一个参数来重新调我那就是你上层需要关注的问题了。
然后我应该抛 Checked Exception 还是 Runtime Exception 取决于我希望你早早就做好方案处理,如果这个错误很可能不可完全避免,那我就抛 Checked Exception,让你在异常发生时去解决他。如果应该这个错误完全可以通过上层代码检测(例如先判断下这个参数是否为 null )来完全避免, 我就会抛 Runtime Exception,表明这个你这里有 bug,传的东西不对,应该提前处理下。
当然这都是我自己的一点理解。就是抛哪种异常取决于我认为这个异常是否应该发生,空指针这种可以用代码提前避免的抛 Runtime Exception , 其他例如 URLencode 这种你很难自己提前判断的,会抛出 Checked Exception 告诉你如果真的出错了 你应该提前写好处理方案。
mandy0119
2019-05-27 11:52:57 +08:00
这是我自己最近在写一个脚手架,想往上抛异常的时候思索该抛什么异常的时候,产生的一点思考。
chendy
2019-05-27 12:04:50 +08:00
9102 年了…这个话题早就结束了吧,CE 的初衷是好的,但是实际效果并不好,所以后来的东西基本能不用就不用了
ethego
2019-05-27 12:07:27 +08:00
wysnylc
2019-05-27 12:24:25 +08:00
然而你只知道 CE 是败笔却没有更好的方案解决
典型的提出问题不提供解决方向,乱扯
EastLord
2019-05-27 12:28:53 +08:00
看这个标题 想起了王垠
xiangyuecn
2019-05-27 13:10:41 +08:00
在用了第三方的代码时:同意“这个机制就是个败笔”,俩种类型谁都可以用(存在选择性往往代表存在歧义),最后成了一锅乱炖😂

鬼才知道有没有人往外面扔了个 Runtime Exception 炸弹😒😒😒,不该用 try catch 的地方也被迫要套个套子才安全😁

最终结果就 java 会趋向于和 C#类似异常机制,任何异常几乎不看源码压根不知道要不要捕获😒
SoloCompany
2019-05-27 13:24:09 +08:00
我支持 kotlin
ce 的接口传染性所引发的问题远比它解决的一点微不足道的问题要严重得多
noli
2019-05-27 13:35:51 +08:00
都 9102 年了还 CE,返回一个 Either 类型不就行了吗,还得动用语法大杀器
xuanbg
2019-05-27 13:44:49 +08:00
@mandy0119 公共库不应该为调用者的错误买单!一个参数不能为空,你非传个 null,我还要告诉你这个参数不能为空?直接抛异常给你自己查去。

当然,这并不友好,但每个模块都做好自己的,就不会出现这种异常。我要说,烂代码很多时间就是被这么惯出来的毛病。
sun1991
2019-05-27 13:46:59 +08:00
语言设计要考虑人的因素. CE 的初衷可能是好的, 但是这样一个精巧的设计需要人力的支持.
对多数人来说, 编程是混口饭吃, 不是什么崇高理想好么, 强迫 CE 的结果就是各种偷懒和绕过的做法导致比没有 CE 更混乱.
xuanbg
2019-05-27 13:57:21 +08:00
@mandy0119 上面的空指针的例子不太好,重新举个栗子。

假设有个参数在你调用的库里面是作为除数使用的,而且文档已经明确地表明该参数不能为 0 亦不可为空。那么该参数为 0 时抛出一个除数不能为 0 的 CE 你觉得你能怎么处理?你也只能抛出异常了事。然后在代码里加上一堆的检查预防该异常的发生,这个时候,CE 和 RE 有多大区别?基本也没啥区别。
mandy0119
2019-05-27 15:32:21 +08:00
@xuanbg 我就是这个意思啊。。。而且很奇怪我是回来看这个帖子才发现你回复了我,竟然没有未读提醒。
然后我对 CE 和 RE 的理解,就是 CE 应该是捕获到了之后可以由程序进行处理,无论是打印特殊日志也好,还是做其他补偿操作重试操作也好,而且是你很可能无法避免这个异常发生,只能就发生了之后做相应操作。
RE 是程序处理不了的,例如我写了个工具类,你传个对象进来我给你做操作,但是我要求这个对象必须要有 id 这个属性,你没有的话,我给你抛出个 CE,catch 到的程序也无法自己去修正,这个时候我抛 RE,让你自己修改这个类,修改完了这个 RE 也不会再复现,你也不用去 try catch 他。
passerbytiny
2019-05-27 16:14:21 +08:00
异常本来就是见仁见智的东西,所以我只指出楼主不对的地方。
一,框架或库对异常的偏好与 Checked Exception 和 Runtime Exception 无关,只取决于它们自身的异常设计理念。Spring 有完整的异常体系,故不再需要你去处理 Checked Exception ; Jackson 很可能是因为仅是库所以不打算自己处理异常,故碰到 IOException 就直接抛给上层。
二,不想处理受检异常的标准做法只有两个:直接抛出它——即添加到方法签名的 throws 中;或者捕获并处理掉它。包装成运行时异常再抛出是一种懒人做法——只在最高层统一处理异常。
三,下层只抛出运行时异常,最高层统一处理异常,这种懒人做法,不是万能药,在该 try catch 的地方,你仍然需要 try catch。如果把这种懒人做法当成标准做法,那 try catch 就完全没必要了。
四,Java 的异常,不是分为受检异常和运行时异常两种,而是首先是异常,然后如果不需要强制捕获则是运行时异常;或者换句话说,异常全部是受检的,其中有部分特殊的(允许在编译时不检测),是运行时异常。如果你看一下 Exception 的继承树,会更好理解这一点。

最后再说一点:Java 是授人以渔的,不是授人以鱼的
liuxey
2019-05-27 16:26:45 +08:00
有没有优雅的处理方法我参考参考

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/567960

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX