选中和未选中的例外和设计思路

时间:2010-03-05 18:47:49

标签: java exception

让我们说有这种不可变的记录类型:

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:

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

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

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

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

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

对不起,我通常不会太在意这个问题(我个人觉得未经检查就好了)。但这实际上是家庭作业中的一个问题,我想把它弄好。“

UPDATE(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违规,并且抛出了未经检查的异常。

这听起来怎么样?!

6 个答案:

答案 0 :(得分:6)

  

请注意,它会导致未经检查   例外。原因是因为这个   是一个经常使用的对象   交易不方便   检查异常。

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

答案 1 :(得分:4)

  

Bloch,第58项:使用已检查的例外   对于可恢复的条件和运行时间   编程错误的例外。

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

答案 2 :(得分:3)

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

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

  1. 您向api的来电者承诺的验证
  2. 预期的异常情况(请求的文件不存在,写入失败等)
  3. 除上述内容外,可恢复性对于确定已检查或未检查的异常也很重要。如果你永远无法恢复(这通常是编程错误中的情况)你可以(或应该?)采取未经检查的异常。

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

    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)) ...部分放在构造函数中。

答案 3 :(得分:2)

真的,这完全取决于期望。如果您希望在正常程序流程下输入无效输入,则应该是一个已检查的异常,以便您可以正常恢复。未经检查的异常是针对程序员无法做任何事情并且不期望正常发生的错误情况。

答案 4 :(得分:1)

如果确实意味着要用于验证器类,那么我希望它在其允许输入范围内具有异常灵活性,仅仅因为它的存在目的是测试有效性,它不能通过抛出异常来做得很好。

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

答案 5 :(得分:1)

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

BTW关于此question的数据库例外以及此question上的一般异常处理的一些很好的建议