代码之家  ›  专栏  ›  技术社区  ›  cchampion

已检查和未检查的异常和设计思想

  •  2
  • cchampion  · 技术社区  · 14 年前

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

    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; } }
        }
    }
    

    注意,它抛出一个未选中的异常。原因是因为这是一个经常使用的对象,并且不方便处理选中的异常。

    但是,如果此对象位于类库中,可以使用它来验证用户输入(或其他一些外部输入)。现在它开始听起来像是一个检查过的异常,因为输入不再由程序员决定了。

    想每个人吗?我应该选中还是不选中,还是有更好的设计?

    更新:

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

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

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

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

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

    在那里,它可能会抛出一个选中的异常,因为输入不再受控制?

    对不起,我通常不会太在意这件事(我个人认为未经检查是可以的)。但这实际上是家庭作业中的一个问题,我想纠正它,哈哈。

    更新(2): 我开始认为我需要的是让validatex抛出一个检查过的异常,因为如果涉及用户输入,通常会使用这个异常。在这种情况下,我们可以再次请求用户输入。但是,对于记录构造函数,它将抛出选中的异常,因为使用无效参数构造记录是违反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; } }
        }
    }
    

    现在,程序员可以在将参数传递到记录类之前验证这些参数。如果没有,则这是一个API冲突,并引发未经检查的异常。

    这听起来怎么样?!

    6 回复  |  直到 11 年前
        1
  •  3
  •   BenMorel Sonaten    11 年前

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

    验证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
  •  6
  •   Jonathan Feinberg    14 年前

    注意它抛出了一个未选中的 例外。原因是这个 是经常使用的对象 而且不方便处理 带有选中的异常。

    不,我认为您之所以在这里抛出未检查的异常是因为它涉及到该API用户违反约定的行为,而不是该方法正常运行期间的异常情况。参见JavaSDK使用的Null PosiExtExchange、ILLCALL AguMutExtExchange等,它们是 运行时异常 因为它们代表违反合同。

        3
  •  4
  •   ptomli    14 年前

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

    如果类的用户能够通过正确地使用API(包括不传递记录为无效的值)来避免该问题,那么这是一个编程错误,因此引发InvalidArgumentException。

        4
  •  2
  •   danben    14 年前

    真的,这都是关于期望。如果您希望在正常程序流下输入一个无效的输入,那么它应该是一个选中的异常,这样您就可以正常恢复。未经检查的异常是针对程序员不能做任何事情并且不希望正常发生的错误情况。

        5
  •  1
  •   ptomli    14 年前

    如果这是 真正地 它是为一个验证器类设计的,所以我希望它在允许输入的范围内具有异常的灵活性,这仅仅是因为它的存在目的是测试有效性,它不能通过抛出异常来很好地测试有效性。

    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;
        }
    }
    
        6
  •  1
  •   Community Reversed Engineer    7 年前

    根据经验,我总是在组件/层/api的公共接口上处理异常。这允许异常冒泡并在顶层处理。- 最后责任时刻 ,同时确保在代码中处理异常,避免 leaky exceptions .

    顺便说一句关于数据库异常的一些好建议 question 以及对此的一般异常处理 question