【问题标题】:Checked and Unchecked Exceptions and design thoughtsChecked 和 Unchecked Exceptions 和设计思想
【发布时间】:2010-03-05 18:47:48
【问题描述】:

假设有这种不可变的记录类型:

public class Record
{
    public Record(int x, int y) {
        Validator.ValidateX(x);
        Validator.ValidateY(y);
        X=x;
        Y=y;
    }

    public final int X;
    public final int Y;

    public static class Validator {
        public void ValidateX(int x) { if(x < 0) { throw new UnCheckedException; } }
        public void ValidateY(int y) { if(y < 0) { throw new UnCheckedException; } }
    }
}

请注意,它会引发未经检查的异常。原因是这是一个经常使用的对象,并且必须处理已检查的异常很不方便。

但是,如果此对象位于类库中,则它可用于验证用户输入(或其他一些外部输入)。现在它开始听起来应该是一个 CHECKED 异常,因为输入不再取决于程序员。

大家想好了吗?我应该勾选还是不勾选,还是有更好的设计?

更新:

我的困惑来自这个场景:通常记录会这样使用:

Record r = new Record(1,2);
OtherObj o = new OtherObj(r);

这取决于程序员,所以未经检查的异常是可以的。

但是,当您从用户那里获取 Record 参数时,您想验证它们对吗?所以你可以打电话给

Record.ValidateX(inputX);
Record.ValidateY(inputY);

因为输入不再受控制,它可能会抛出已检查异常?

抱歉,我通常不会太在意这个(我个人认为未选中就可以了)。但这实际上是家庭作业中的一个问题,我想把它做好,哈哈。

更新(2): 我开始认为我需要的是 ValidateX 抛出一个检查异常,因为如果涉及用户输入,通常会使用它。在这种情况下,我们可以再次要求用户输入。但是,对于 Record 构造函数,它将抛出已检查的异常,因为构造带有无效参数的 Record 是违反 API 的。新代码如下所示:

public class Record
{
    public Record(int x, int y) {
        try
        {
            Validator.ValidateX(x);
            Validator.ValidateY(y);
        }catch(CheckedException xcpt) { throw new UnCheckedException(xcpt.getMessage()); }
        X=x;
        Y=y;
    }

    public final int X;
    public final int Y;

    public static class Validator {
        public void ValidateX(int x) throws CheckedException { if(x < 0) { throw new CheckedException; } }
        public void ValidateY(int y) throws CheckedException { if(y < 0) { throw new CheckedException; } }
    }
}

现在程序员可以在将参数传递给 Record 类之前验证参数。如果没有,那么它就是一个 API 违规,并抛出一个未经检查的异常。

这听起来怎么样?!

【问题讨论】:

  • 真的 是一个 Validator 类,还是 SO 的示例只是这样表述的?如果它实际上是一个验证器,那么我希望它通过其正常的 isValid/isNotValid 方法简单地指示无效输入。 EG:如果你的 'x' 是 Integer,那么即使 null 也不应该抛出任何东西,仅仅表明传递的值是无效的。
  • Validate[XY] 的目的是告诉您所提供的值是否合适,或者这只是不幸的命名?更具体地说,Validate[XY] 的文档对有效输入值和提供无效输入的后果有什么看法?如果它说当你传入一个

标签: java exception


【解决方案1】:

注意它会抛出一个未经检查的 例外。原因是因为这个 是一个经常使用的对象 而且不得不处理很不方便 有一个检查异常。

不,我认为您在这里抛出未经检查的异常的原因是因为它涉及该 API 的用户违反合同,而不是该方法正常运行期间的异常情况。查看 Java SDK 对 NullPointerException、IllegalArgumentException 等的使用,它们是 RuntimeExceptions,因为它们代表了违反合同。

【讨论】:

  • 如果合同明确记录在类文档中会更好——但为了简洁起见,我假设它们被省略了。
【解决方案2】:

布洛赫,第 58 条:使用检查的异常 对于可恢复的条件和运行时间 编程错误例外。

如果您的类的用户能够通过正确使用 API 来避免该问题(包括不传入记录为无效的值),那么这是一个编程错误,所以抛出一个 InvalidArgumentException。

【讨论】:

    【解决方案3】:

    您永远不应该使用未经检查的异常来验证用户输入。通常会检查异常,因为这样您就不会忘记处理异常。

    在 api 中使用的验证参数是一种完全不同的类型。这些应该被取消选中,否则你最终会在每个函数调用中添加 try/catch。每当向方法传递一个无效参数时,这肯定是一个编程错误。正常的方法是抛出 IllegalArgumentException 或 NullPointerException(或任何其他适合您需要的)。 在 api 调用中留下检查的异常

    1. 您向 api 调用者承诺的验证
    2. 预期的异常情况(请求的文件不存在、写入失败等)

    除了上述之外,可恢复性对于决定检查或未经检查的异常也很重要。如果您永远无法恢复(这通常是编程错误的情况),您可以(或应该?)接受未经检查的异常。

    最好不要编写不符合上述准则的代码。对您来说,这意味着您必须编写一个函数,该函数在用于验证用户输入时返回布尔或检查异常,并在验证函数的输入参数时返回未经检查的异常。当然,您可以并且应该使用相同的函数来验证异常,只需包装返回布尔或检查异常的函数:

    public boolean validateX(int x)
    {
         return x > 0;
    }
    
    private void validateParameter(int x)
    {
         if (validateX(x))
         {
             throw new IllegalArgumentException("X is invalid");
         }
    }
    

    这是一个多一点的工作,但它会给你两全其美。通过将 validate 参数函数设为私有,您可以确保不会在课堂之外意外使用它。当然你也可以将if(validateX(x)) ... 部分放在你的构造函数中。

    【讨论】:

    • 我想你刚刚告诉了我我在最新更新中添加的内容 (2)。检查一下,让我知道我是否正确地按照您在此处描述的操作!!!谢谢你!!!
    • 是的,你是对的。作为额外提示:将异常重新抛出为另一个异常时。使用接受异常的构造函数,这样会保留更多信息(例如您的堆栈)作为新异常。
    【解决方案4】:

    真的,一切都与期望有关。如果您预计在正常程序流程下可能会输入无效输入,则它应该是一个检查异常,以便您可以优雅地恢复。未经检查的异常适用于程序员无能为力且不希望正常发生的错误情况。

    【讨论】:

      【解决方案5】:

      如果这真的是为了一个验证器类,那么我希望它在允许输入的范围上异常灵活,仅仅是因为它存在的目的是测试有效性,这它不能通过抛出异常来做得很好。

      public interface Validator<T> {
          public boolean isValid(T object);
      }
      
      public final class PositiveIntegerValidator implements Validator<Integer> {
          public boolean isValid(Integer object) {
              return object != null && object > 0;
          }
      }
      

      【讨论】:

      • 不,这只是一个例子。真正的问题是整数必须介于某些值之间,例如 0 > x
      • 如果你手头有无效值,你能纠正这个问题吗?例如通过 UI 向用户询问更合适的值。
      • 是的,您可以通过要求更合适的值来纠正它们
      【解决方案6】:

      根据经验,我总是在我的组件/层/api 的公共接口处处理异常。这允许异常冒泡并在顶层进行处理 - Last Responsible Moment,同时确保我在代码中处理异常,避免 leaky exceptions

      顺便说一句,关于 question 上的数据库异常和 question 上的一般异常处理的一些极好的建议

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-12-27
        • 2014-11-04
        • 1970-01-01
        • 2011-07-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-05
        相关资源
        最近更新 更多