Java - 如何只创建具有有效属性的对象?

Tia*_*que 17 java validation constructor object

我正在做一个基本的Java课程,我遇到了一个问题:如果我已经将有效参数传递给构造函数,我该如何创建一个对象?

在实现验证后,我应该创建一个替代类并从那里调用构造函数吗?

或者我应该/可以在类中使用静态方法进行验证吗?

在这种情况下,最佳做法是什么?

ass*_*ias 28

标准做法是验证构造函数中的参数.例如:

class Range {
  private final int low, high;
  Range(int low, int high) {
    if (low > high) throw new IllegalArgumentException("low can't be greater than high");
    this.low = low;
    this.high = high;
  }
}
Run Code Online (Sandbox Code Playgroud)

附注:要验证参数不是null,这是相当常见的,您可以使用:

import static java.util.Objects.requireNonNull;

Constructor(Object o) {
  this.o = requireNonNull(o); //throws a NullPointerException if 'o' is null
}
Run Code Online (Sandbox Code Playgroud)

UPDATE

回复您关于社会安全号码的具体评论.一种方法是在类中添加一个方法:

//constructor
public YourClass(String ssn) {
  if (!isValidSSN(ssn)) throw new IllegalArgumentException("not a valid SSN: " + ssn);
  this.ssn = ssn;
}

public static boolean isValidSSN(String ssn) {
  //do some validation logic
}
Run Code Online (Sandbox Code Playgroud)

调用代码可能如下所示:

String ssn = getSsnFromUser();
while(!YourClass.isValidSSN(ssn)) {
  showErrorMessage("Not a valid ssn: " + ssn);
  ssn = getSsnFromUser();
}
//at this point, the SSN is valid:
YourClass yc = new YourClass(ssn);
Run Code Online (Sandbox Code Playgroud)

通过这种设计,您实现了两件事:

  • 你在使用它之前验证了用户输入(你应该经常这样做 - 用户非常擅长打字错误)
  • 你已经确定如果YourClass被滥用会引发异常并且它将帮助你检测错误

您可以通过创建一个SSN包含SSN 的类并封装验证逻辑来​​进一步完善.YourClass然后接受一个SSN对象作为一个参数,它始终是一个有效的SSN构造.

  • 为避免空值,我会使用@NotNull注释 (5认同)
  • +1为您的更新.我教给年轻编码员的规则之一是"异常处理是出于特殊情况." 在正确处理SSN并要求更正方面没有任何耻辱,然后让构造函数检查并处理任何意外情况.我唯一能做其他事情的地方是性能关键代码,其中检查可能占用总运行时预算的很大一部分.在这些情况下,Danikov的回答"不要使用异常进行流量控制"成为正确的解决方案,我将使用流量控制处理所有内容,并且没有例外. (3认同)

Mur*_*nik 9

我只是IllegalArgumentException在构造函数本身中抛出一个:

public class MyClass {
    private int i;

    public MyClass (int i) {
        // example validation:
        if (i < 0) {
            throw new IllegalArgumentException ("i mustn't be negatve!");
        }
        this.i = i;
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*kov 7

编程中众所周知的真理是"不要使用例外进行流量控制".您的代码应该在调用构造函数之前了解这些限制并防范它们,而不是处理错误.预期情况存在例外情况,特别是那些无法预测或防范的情况(例如,尽管在上次检查期间可以正常,但IO流可能在写入期间变为无效).

虽然您可以在构造函数中抛出异常,但这并不总是理想的.如果您正在编写您希望被其他人使用/重用的公共对象,则异常是公共构造函数的唯一真正选项,但是这些限制及其结果(例如将抛出的异常)应该在javadoc中清楚地记录下来.类.

对于内部类,断言更合适.正如Oracle所述:"断言......应该用于检查永远不会发生的情况,检查有关数据结构的假设,或对私有方法的参数强制执行约束." - 在Java技术中使用断言.您可能仍应记录您对该类的期望,但您的应用程序应在内部进行任何检查,而不是依赖于抛出的任何异常.

静态工厂方法可以帮助一点,它们的好处正在详细阐述另一个问题:如何使用"静态工厂方法"而不是构造函数.但是,当事情无效时(或返回null,信息量较少),它们不再提供强有力的验证选项.

理想的解决方案是Builder模式.它不仅可以在管理参数时提供更大的灵活性,还可以单独验证每个参数,或者使用可以一次评估所有字段的验证方法.构建器可以并且应该用于隐藏对象的实际构造函数,享受对它的唯一访问并防止任何不需要的值被提交,而断言可以防止"构建器永远不应该提交这些值".