反对已检查异常的案例

多年来,我一直无法对以下问题找到一个恰当的答案:为什么一些开发人员如此反对检查异常?我有过无数次的对话,在博客上读过一些东西,读过布鲁斯·埃克尔(Bruce Eckel)说过的话(我看到的第一个公开反对他们的人)

我目前正在编写一些新代码,并非常仔细地注意如何处理异常。我正试图了解的观点是;我们不喜欢检查例外情况”;人群拥挤,我仍然看不见

我的每一次谈话都以同一个问题结束,而这个问题没有得到回答。。。让我来设置一下:

总的来说(从Java的设计方式来看)

  • 错误是指不应该被抓到的东西(VM对花生过敏,有人把一罐花生掉在上面)
  • RuntimeException是针对程序员做错的事情(程序员从数组末尾走出来)
  • Exception(除了RuntimeException)用于程序员无法控制的事情(写入文件系统时磁盘已满,进程的文件句柄已达到限制,您无法再打开任何文件)
  • Throwable只是所有异常类型的父级

我听到的一个常见论点是,如果发生异常,那么开发人员要做的就是退出程序

我听到的另一个常见论点是,检查异常会使重构代码变得更加困难

至于;我要做的就是退出“;论点我说,即使您退出,您也需要显示一条合理的错误消息。如果你只是在处理错误上下赌注,那么当程序在没有明确说明原因的情况下退出时,你的用户不会太高兴

至于;这使得重构变得很困难;群组,这表明没有选择适当的抽象级别。与其声明一个方法抛出一个IOException,不如将IOException转换成一个更适合当前情况的异常

使用catch(Exception)(或者在某些情况下使用catch(Throwable)包装Main以确保程序可以正常退出,我没有任何问题,但是我总是捕获我需要的特定异常。这样做至少可以显示适当的错误消息

人们永远不会回答的问题是:

如果抛出RuntimeException
子类而不是Exception
子类,那么你怎么知道呢
你应该接住吗

如果答案是catchException,那么您处理程序员错误的方式与处理系统异常的方式相同。我认为这是错误的

如果您捕获了Throwable,那么您正在以相同的方式处理系统异常和VM错误(以及类似的错误)。这在我看来是错误的

如果答案是只捕获已知抛出的异常,那么如何知道抛出了哪些异常?当程序员X抛出一个新异常并忘记捕获它时会发生什么?这对我来说似乎非常危险

我想说,显示堆栈跟踪的程序是错误的。不喜欢检查异常的人不会有这种感觉吗

所以,如果你不喜欢检查异常,你能解释一下为什么不喜欢,并回答一个没有得到回答的问题吗

我不是在寻找关于何时使用这两种模型的建议,我要寻找的是为什么人们从RuntimeException扩展,因为他们不喜欢从异常扩展,和/或为什么他们捕获异常,然后重试RuntimeException,而不是向他们的方法添加抛出。我想忍受不喜欢检查异常的动机

我想我读的是和你一样的Bruce Eckel采访,这总是困扰着我。事实上,这个论点是由被采访者(如果这确实是你所说的帖子)Anders Hejlsberg提出的,他是.NET和C#背后的微软天才

http://www.artima.com/intv/handcuffs.html

虽然我是海尔斯伯格及其作品的粉丝,但这一论点一直让我觉得是假的。它基本上可以归结为:

&“检查过的异常是不好的,因为程序员只是滥用它们,总是捕获它们并将其丢弃,这会导致隐藏和忽略问题,否则这些问题会呈现给用户”

通过“以其他方式呈现给用户”我的意思是,如果使用运行时异常,懒惰的程序员将忽略它(而不是使用空的catch块捕获它),用户将看到它

这个论点的总结是程序员不会正确地使用它们,不正确地使用它们比没有它们更糟糕

这个论点有些道理,事实上,我怀疑高斯林不在Java中使用运算符重写的动机来自一个类似的论点——它们让程序员感到困惑,因为它们经常被滥用

但最后,我发现这是海尔斯伯格的一个虚假论点,可能是一个事后的论点,用来解释这一缺失,而不是一个深思熟虑的决定

我认为,虽然过度使用检查异常是一件坏事,而且往往会导致用户处理不及时,但正确使用它们可以让API程序员为API客户机程序员带来巨大的好处

现在,API程序员必须小心不要到处抛出检查过的异常,否则它们只会惹恼客户机程序员。非常懒惰的客户机程序员会像Hejlsberg警告的那样求助于catch(Exception){},所有的好处都将失去,地狱也将接踵而至。
但在某些情况下,没有什么可以替代良好的检查异常

对我来说,典型的例子是文件开放API。语言史上的每一种编程语言(至少在文件系统上)都有一个API,可以让你打开一个文件。每个使用这个API的客户端程序员都知道,他们必须处理他们试图打开的文件不存在的情况。
让我重新表述一下:每个使用此API的客户机程序员都应该知道他们必须处理这种情况。
还有一个问题:API程序员是否可以帮助他们知道应该通过单独评论来处理它,或者他们是否可以坚持客户处理它

在C语言中,这个成语的意思是

如果(f=fopen("goodluckfindingthisfile"){…}
否则{//找不到文件。。。

其中,fopen通过返回0表示失败,而C(愚蠢地)让你将0视为一个布尔值……基本上,你学会了这个习惯用法,你就没事了。但是如果你是个傻瓜,你没有学会这个习惯用法呢。当然,你从

f=fopen(“GoodLuckFindingThis文件”);
f、 read();//砰!

并通过艰苦的方式学习

请注意,我们这里只讨论强类型语言:对于强类型语言中的API有一个清晰的概念:它是一个功能(方法)的大杂烩,供您使用,并为每种语言使用一个明确定义的协议

该明确定义的协议通常由方法签名定义。
这里,fopen要求您向它传递一个字符串(在C的情况下是一个char*)。如果您向它传递其他内容,则会出现编译时错误。您没有遵守协议-您没有正确使用API

在某些(模糊的)语言中,返回类型也是协议的一部分。如果在某些语言中尝试调用等价的fopen(),而不将其分配给变量,则也会出现编译时错误(只能使用void函数执行此操作)

我想指出的一点是:在静态类型语言中,API程序员鼓励客户机正确使用API,防止客户机代码在出现任何明显错误时编译。

(在动态类型语言(如Ruby)中,您可以传递任何东西,比如浮点,作为文件名,它将被编译。如果您甚至不打算控制方法参数,为什么要用选中的异常来麻烦用户呢?这里的参数只适用于静态类型语言。)

那么,检查异常呢

这里有一个可以用来打开文件的JavaAPI

试试看{
f=新文件输入流(“GoodLuckFindingThis文件”);
}
catch(filenotfounde异常){
//处理它。不,真的,处理它!
…//这是我在处理的
}

看到那个陷阱了吗?下面是该API方法的签名:

公共文件输入流(字符串名称)
抛出FileNotFoundException

请注意,FileNotFoundException是一个选中的异常

API程序员对您说:
"您可以使用此构造函数创建新的FileInputStream,但

a) 必须将文件名作为
字符串
b) 必须接受
文件可能不存在的可能性
“在运行时找到”

这就是我所关心的全部问题

基本上,关键是问题所说的“程序员无法控制的事情”。我的第一个想法是,他/她指的是API程序员无法控制的事情。但事实上,正确使用检查异常时,实际上应该是指客户端程序员和API都无法控制的事情程序员的控制。我认为这是不滥用检查异常的关键

我认为打开的文件很好地说明了这一点。API程序员知道,您可能会给他们一个在调用API时不存在的文件名,他们将无法返回您想要的,但将不得不抛出一个异常

发表评论