由于这方面存在很多歧义,我们能否提出一些清晰,简单且易于使用的规则?
这是第一次尝试,这三条规则应该适用于所有情况。
第一条规则基于以下共同原则:方法的实现细节不应暴露给调用者:
规则1: 检查异常不应向调用者公开实现细节: 你可以这样测试它:如果你完全改变了实现,但仍然希望方法能够履行它的契约(比如在重构中),抛出异常仍然有意义吗? 如果是,那么它应该是一个Checked异常,因为它不是实现细节,而是该方法fullfills的用例的一部分。 如果不是,那么应该取消选中该异常,因为它是一个不应该向调用者公开的实现细节。
如果基于规则1,则应检查例外,然后应用规则2:
规则2 :由于已检查的异常是方法合同的一部分,因此违反方法的合同不能成为已检查的异常。 例如,此方法从其参数执行sql查询:
executeSqlQuery(String sqlQuery)
如果调用者提供的字符串不是sql查询,那么他违反了方法的输入条件,并且必须取消选中生成的异常。
如果基于规则2,异常似乎仍然被检查,则应用规则3来检查该方法是否遵循另一种最佳实践:单一责任原则
规则3 :方法应该只做一件事。 例如方法
List.get(int index)
正确地做了一件事:从列表中返回所需的元素。而不是试图完成另一个常见的用例:检查元素是否存在。 另一方面,方法
findById(Long id) throws RecordNotFoundException
错误地尝试混合这两个函数,并强制所有调用者在找不到记录时始终考虑用例,即使他们知道记录存在。
是否缺少一些规则,还是我们可以进一步简化和澄清规则?
背景 关于这个主题有很多不同的看法。有很多“激进”解决方案的支持者,比如永远不会使用已检查的异常(一些Java书籍或C#的设计者),或者几乎所有东西都应该是一个经过检查的异常(java.io包中的IOException或JDBC中的SQLException)。 并且至少相同数量的观点建议使用这两种类型但是提供主观和模糊的规则来决定它们之间没有经常缺失的冗长解释而不是帮助带来更多的混淆(例如:如果调用者是合理的从异常中恢复到应该检查。再次尝试呼叫是否合理恢复?对于某些客户来说可能是这样。合理且恢复的词语太模糊了。)
答案 0 :(得分:3)
这里是底线指南:如果可以合理地期望客户端从异常中恢复,请将其作为检查异常。如果客户端无法执行任何操作以从异常中恢复,请将其设置为未经检查的异常。
对我来说,这看起来是一个非常明确,简单的规则。
编辑:您的背景编辑很好地解释了您的困惑和挫败感。如果你愿意,我可以提出一个更简单的规则:
始终可用。尽可能实用。力求保持一致。
关键是异常处理是可能抛出的方法的用户必须做的事情。遗憾的是,Java强迫编写该方法的人为该方法的用户做出选择。因此,我略微倾向于"从未做过检查"因为你的方法总是可用。对于某些情况,例如网络I / O,强制用户建立一些错误处理可能真的是实用,但事实并非如此。如果您问自己 hm,我的最后400个方法都抛出了RuntimeError
,那么现在这会抛出一个检查过的异常吗?,获得一致性可能会使使用你的库更多实用。
可悲的是,这个规则更容易,但不是更清晰,因为至少实用和一致是非常主观的概念。所以你在这里,坚持一些非常 style 的问题。
答案 1 :(得分:1)
Java处理已检查异常的方式的基本问题是,尽管区分出于预期原因而抛出的异常会很有用(例如getRecordById
在给定的ID没有&#时抛出RecordNotFoundException
39; t存在)来自因意外原因而被抛出的那些(例如,一个方法调用getRecordId
表示存在但不存在的ID,类型异常与它是否来自方法是出于方法所知的原因,还是因为它在嵌套方法调用中被意外抛出而无关。
如果Java允许将异常实例标记为"预期"或者"意外",用于捕获预期和意外的语句的不同语句,以及指示块内的任何方法都不会抛出某些异常的方法(如果有任何此类异常,则抛出标记为"期望",它们应该被标记为"意外"在传播之前)然后将某些方法声明为抛出"期望"例外是有用的。然而,鉴于目前的架构,将大多数例外声明为未选中是更好的。