检查异常的情况

问题:

多年以来,我一直无法得到一个正确的答案,以下问题:为什么一些开发人员反对检查异常?我有很多对话,在博客上阅读,阅读布鲁斯埃克尔不得不说(我看到的第一个人对他们说话)。
我正在编写一些新的代码,并非常注意我如何处理异常。我试图看到“我们不喜欢检查例外”人群的观点,我仍然看不到。
每个对话我都以同样的问题结束,没有得到答复…让我设置一下:
一般来说(从Java的设计方式来看)

  • 错误是为了永远不会被捕获的东西(VM有花生过敏,有人掉了一罐花生)
  • RuntimeException是为程序员做错了(程序员走出数组的结尾)
  • 异常(RuntimeException除外)适用于程序员控制之外的情况(写入文件系统时磁盘填满,已达到该进程的文件句柄限制,您无法再打开任何文件)
  • Throwable只是所有异常类型的父级。

我听到的一个常见的观点是,如果发生异常,那么所有的开发者都要做的是退出程序。
我听到的另一个常见的观点是,检查的异常使得重构代码变得更加困难。
对于“所有我要做的是退出”参数,我说即使你退出,你需要显示一个合理的错误消息。如果您正在处理错误,那么当程序退出时,您的用户将不会过分乐意,而且没有明确指出为什么。
因为“使它难以重构”人群,这表明没有选择适当的抽象层次。而不是声明一个方法抛出IOException,IOException应该被转换成一个更适合于正在发生的异常。
我没有问题包装主要与catch(异常)(或在某些情况下捕获(Throwable),以确保程序可以正常退出 – 但我总是捕捉到我需要的特定异常,这样做允许我,至少显示一个相应的错误信息。
人们永远不会回答的问题是这样的:

如果您抛出RuntimeException子类而不是Exception子类,那么您如何知道应该捕获什么?

如果答案是捕获异常,那么你也是以与系统异常相同的方式处理程序员的错误。这对我来说似乎是错误的。
如果您捕获Throwable,那么您将以相同的方式处理系统异常和VM错误(等)。这对我来说似乎是错误的。
如果答案是你只捕获你知道的异常,那么你怎么知道抛出什么呢?当程序员X抛出一个新的异常并忘记抓住它会发生什么?对我来说似乎很危险
我会说一个显示堆栈跟踪的程序是错误的。不喜欢检查异常的人不觉得这样吗?
所以,如果你不喜欢被检查的异常,你可以解释为什么不回答没有得到回答的问题吗?
编辑:我不是在寻找什么时候使用任何一个模型的建议,我正在寻找的是why人从RuntimeException扩展,因为他们不喜欢从Exception扩展和/或为什么他们捕获异常,然后重新抛出一个RuntimeException而不是添加throws到他们的方法。我想了解不喜欢被检查异常的动机。

回答:

我想我读过你所做过的同样的布鲁斯·埃克尔采访,而且总是让我感到烦恼。实际上,被访者的观点(如果这确实是你所说的话),Anders Hejlsberg是.NET和C#背后的MS天才。

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

范,虽然我是Hejlsberg和他的工作,这个论点总是让我觉得虚伪。它基本归结为:

“检查的异常是坏的,因为程序员只是通过总是捕获它们并且将它们解散而使其被滥用,导致隐藏和忽略的问题,否则这些问题将被呈现给用户”。

通过“otherwise presented to the user”我的意思是,如果您使用运行时异常,那么懒惰的程序员将会忽略它(与捕获它的空catch块),用户将看到它。
论点摘要的总结就是“Programmers wont use them properly and not using them properly is worse than not having them”
这个论点有一些道理,实际上我怀疑Gosslings的动机不是在Java中引用操作符,而是来自类似的观点 – 他们会混淆程序员,因为它们经常被滥用。
但是最后我发现这是Hejlsberg的一个虚构的论据,也许是为了解释缺乏而不是一个深思熟虑的决定而创建的。
我会认为,尽管过度使用被检查的例外是一件坏事,并且倾向于导致用户的恶意处理,但正确使用它们可以使API程序员对API客户端程序员有很大的好处。
现在API程序员必须小心,不要在所有地方抛出被检查的异常,或者他们会简单地烦恼客户程序员。非常懒惰的客户端程序员将诉诸于(Exception) {},因为Hejlsberg警告,所有的好处将会丢失,并且将会发生。
但在某些情况下,只能替代一个很好的检查异常。
对我而言,典型的例子是文件开放的API。语言历史上的每种编程语言(至少在文件系统上)都有一个API,可以让您打开一个文件。并且使用此API的每个客户端程序员都知道他们必须处理他们尝试打开的文件不存在的情况。
让我再说一遍:每个客户端程序员使用这个API should know来处理这种情况。
还有一个问题:API程序员可以帮助他们知道他们应该通过单独评论来处理它,或者他们确实insist客户端处理它。
在C中,成语就是这样

  if (f = fopen("goodluckfindingthisfile")) { ... } 
  else { // file not found ...

其中fopen表示失败返回0和C(愚蠢)可以让你将0作为一个布尔值,…基本上你学习这个成语,你没事。但是,如果你是一个noob,你没有学习成语。那么当然你开始了

   f = fopen("goodluckfindingthisfile");
   f.read(); // BANG! 

并学习困难的方式。
请注意,我们只在谈论强类型语言:明确了一种强类型语言中的API:它是一种功能(方法)的smorgasbord,您可以为每个语言使用明确定义的协议。
这个明确定义的协议通常由方法签名定义。
这里fopen要求您传递一个字符串(或在C的情况下为char *)。如果你给别的东西,你会得到一个编译时错误。您没有遵循协议 – 您没有正确使用API​​。
在某些(模糊)语言中,返回类型也是协议的一部分。如果您尝试在某些语言中调用相当于fopen()而不将其分配给变量,那么您也将获得编译时错误(只能使用void函数)。
我想要做的是:In a statically typed language the API programmer encourages the client to use the API properly by preventing their client code from compiling if it makes any obvious mistakes.
(在一个动态类型的语言中,像Ruby一样,你可以传递一个浮动文件作为文件名,并且它将被编译,如果你甚至不去控制方法参数,为什么麻烦用户检查异常这里提出的论据仅适用于静态类型的语言。)
那么检查异常呢?
那么这里是可用于打开文件的Java API之一。

try {
  f = new FileInputStream("goodluckfindingthisfile");
}
catch (FileNotFoundException e) {
  // deal with it. No really, deal with it!
  ... // this is me dealing with it
}

看到那个catch?以下是该API方法的签名:

public FileInputStream(String name)
                throws FileNotFoundException

请注意,FileNotFoundExceptionchecked例外。
API程序员对你说:
  “你可以使用这个构造函数创建一个新的FileInputStream,但是你
a)must将文件名传递为a
    字符串
b)must接受
    文件可能没有的可能性
    在运行时找到“
这就是我所关心的一切。
关键是基本上是什么问题所说的“程序员控制的东西”。我的第一个想法是他/她的意思是API程序员控制的东西。但事实上,使用正确的检查异常应该是客户端程序员和API程序员控制之外的事情。我认为这是不滥用检查异常的关键。
我认为文件打开很好地说明了这一点。 API程序员知道你可能会给他们一个文件名,在API被调用时证明是不存在的,并且他们将无法返回你想要的东西,而是必须抛出异常。他们也知道这将会非常经常地发生,客户端程序员可能期望文件名在写入该调用时是正确的,但是由于超出其控制的原因,它在运行时可能是错误的。
所以API使它显而易见:当你打电话给我这个文件不存在的情况下,你已经很好地处理了这个文件。
一个反案例会更清楚。想象一下,我正在写一个表API。我有一个API的表模型,包括这个方法:

public RowData getRowData(int row) 

现在,作为一个API程序员,我知道有些客户端会在表的外面传递行或行值的负值。所以我可能会试图抛出一个被检查的异常并强制客户端处理它:

public RowData getRowData(int row) throws CheckedInvalidRowNumberException

(我当然不会称它为“检查”)。
这是检查异常的不良用法。客户端代码将要充满调用来获取行数据,每个数据将要使用一个try / catch,以及什么?他们会向用户报告错误的行被寻求吗?可能不是 – 因为我的桌面视图周围的UI是不应该让用户进入非法行被请求的状态。所以这是客户端程序员的错误。
API程序员仍然可以预测客户端将编码这样的错误,并应该使用像IllegalArgumentException这样的运行时异常处理它。
getRowData中有一个被检查的异常,这显然是一种情况,这将导致Hejlsberg的懒惰程序员只是添加空的捕获。当这种情况发生时,即使对测试者或客户端开发人员进行调试,非法的行值也不会明显,而是会导致难以确定源的错误。阿里安火箭将在发射后爆炸。
好的,所以这里的问题是:我在说,检查的异常FileNotFoundException不仅仅是一件好事,而且是API程序员工具箱中用于为客户端程序员最有用的方式定义API的一个重要工具。但是CheckedInvalidRowNumberException是一个很大的不便,导致编程不好,应该避免。但是怎么说差别呢
我想这不是一个确切的科学,我猜这是Hejlsberg的论据的基础,也许在某种程度上证明了这一点。但是我不会很高兴把宝宝从这里洗澡,所以让我在这里提出一些规则来区别出色的检查例外:

  1. 超出客户控制权或已关闭与开放:
    检查的异常只能在客户端程序员的API and失控的情况下使用。
    这与系统的openclosed如何相关。在客户程序员可以控制的constrained UI中,比如说所有的按钮,键盘命令等,从表格视图(一个封闭的系统)中添加和删除行,它是一个客户端编程错误,如果它试图获取来自不存在行的数据。在基于文件的操作系统中,任何数量的用户/应用程序可以添加和删除文件(开放系统),可以想象,客户端请求的文件已经被删除,而不知道他们应该如何处理它。
  2. 无处不在:
    检查的异常不应该在客户端频繁执行的API调用中使用。
    经常我的意思是从客户端代码中的很多地方 – 不是经常在时间。所以一个客户端代码并不倾向于尝试打开相同的文件很多,但是我的表格视图从不同的方法得到RowData。特别是我要写很多代码
    if(model.getRowData()。getCell(0).isEmpty())

    每次都要包装try / catch将是痛苦的。
  3. 通知用户:
    在您可以想像向最终用户呈现有用的错误消息的情况下,应使用检查异常。
    这是上面提到的“and what will you do when it happens?”问题。它也涉及到项目1.由于您可以预测您的客户端API系统之外的某些内容可能导致该文件不在,您可以合理地告诉用户:

     “错误:找不到文件'goodluckfindingthisfile'”
     
    由于您的非法行号是由内部错误引起的,并且没有用户的错误,所以真的没有可用的信息。如果您的应用程序不允许运行时异常通过控制台,那么最终可能会给他们一些丑陋的消息,如:
    
     “发生内部错误:...中的IllegalArgumentException”
     
    简而言之,如果您不认为您的客户端程序员可以以帮助用户的方式解释您的异常,那么您应该不会使用已检查的异常。
所以这些是我的规则。有些设计,无疑将是例外(请帮助我改进,如果你愿意)。但是我的主要观点是有一些例如FileNotFoundException,其中被检查的异常与参数类型的API合同一样重要和有用。所以我们不应该因为滥用而放弃它。 对不起,这并不意味着这么长时间,而且很吝啬。让我完成两个建议: A:API程序员:谨慎使用检查的例外来保护它们的有用性。当有疑问时使用未经检查的异常。 B:客户程序员:在开发过程的初期就习惯于创建一个包装的异常(google it)。 JDK 1.4及更高版本为此提供RuntimeException中的构造函数,但您也可以轻松创建自己的构造函数。这是建筑物:
public RuntimeException(Throwable cause)

然后习惯每当你必须处理一个被检查的异常,你感到懒惰(或者你认为API程序员过度使用首选的检查异常),不要只是吞下异常,包装它并推翻它。

try {
  overzealousAPI(thisArgumentWontWork);
}
catch (OverzealousCheckedException exception) {
  throw new RuntimeException(exception);  
}

把它放在你的一个IDE的小代码模板中,当你感到懒惰时使用它。这样,如果您真的需要处理检查的异常,则在运行时看到问题后,您将被迫回来处理它。因为相信我(Anders Hejlsberg),你永远不会再回到那个TODO

catch (Exception e) { /* TODO deal with this at some point (yeah right) */}

 
 
Code问答: http://codewenda.com/topics/python/
Stackoverflow: The case against checked exceptions

*转载请注明本文链接以及stackoverflow的英文链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注

− 7 = 1