Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/cmake/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java-如何仅创建具有有效属性的对象?_Java_Validation_Object_Constructor - Fatal编程技术网

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

Java-如何仅创建具有有效属性的对象?,java,validation,object,constructor,Java,Validation,Object,Constructor,我正在学习一门基本的Java课程,我遇到了一个问题:只有在我向构造函数传递了有效参数的情况下,我如何创建一个对象 我应该创建一个备用类,并在验证实现后从那里调用构造函数吗 或者我应该/可以在类中使用静态方法进行验证 这种情况下的最佳实践是什么?构造函数可以抛出异常(请参阅),因此,如果传递了无效值,您可以让构造函数抛出异常。您还可以将构造函数设置为私有,并使用静态方法创建执行检查的对象。这可能会更干净。我只需在构造函数中添加一个: public class MyClass { priva

我正在学习一门基本的Java课程,我遇到了一个问题:只有在我向构造函数传递了有效参数的情况下,我如何创建一个对象

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

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


这种情况下的最佳实践是什么?

构造函数可以抛出异常(请参阅),因此,如果传递了无效值,您可以让构造函数抛出异常。您还可以将构造函数设置为私有,并使用静态方法创建执行检查的对象。这可能会更干净。

我只需在构造函数中添加一个:

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;
}
公共类MyClass{
私人互联网i;
公共MyClass(int i){
//验证示例:
if(i<0){
抛出新的IllegalArgumentException(“我不能是否定的!”);
}
这个。i=i;
}

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

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;
  }
}
旁注:要验证参数不为null(这是很常见的),可以使用:

import static java.util.Objects.requireNonNull;

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

更新

回复您关于社会保险号码的具体评论。一种方法是向类中添加方法:

//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
}
调用代码可能如下所示:

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);
通过这种设计,您实现了两件事:

  • 在使用之前验证用户输入(您应该始终这样做-用户非常擅长打字)
  • 您已经确定,如果
    YourClass
    被误用,将抛出异常,它将帮助您检测bug

您可以更进一步,创建一个
SSN
类来保存SSN并封装验证逻辑。
YourClass
然后会接受一个
SSN
对象作为参数,该参数通过构造始终是有效的SSN。

从构造函数中抛出异常是一种不好的做法。您最终会得到一个部分初始化这可能会破坏各种契约


如果构造函数不是对所有输入组合都有效,那么创建一个进行验证的工厂方法,并将构造函数设置为私有的更为简洁。如果确实存在失败的可能性(也就是说,失败不是由于编程错误),然后返回一个
可选的

可能是合适的。编程中的一个众所周知的真理是“不要将异常用于流控制”。在调用构造函数之前,您的代码应该了解这些限制并加以防范,而不是处理错误。在预期情况下会出现异常,尤其是在无法预测或防范at(例如,IO流在写入期间可能会变得无效,尽管在上一次检查期间是正常的)

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

对于内部类,断言更合适。正如Oracle所说:“断言……应该用于检查不应该发生的情况,检查关于数据结构的假设,或者对私有方法的参数实施约束。”-。您可能仍然应该记录您对类的期望,但是您的应用程序应该事先在内部执行任何检查,而不是依赖于抛出的任何异常

静态工厂方法可以帮上一点忙,它们的好处是通过另一个问题来阐述的:。但是,它们不提供强验证选项,如果情况无效(即,返回null,信息量较小),它们也不依赖异常


理想的解决方案是。它不仅允许在管理参数时有更大的灵活性,还可以单独验证每个参数,或者有一个可以一次计算所有字段的验证方法。生成器可以而且应该用于隐藏对象的实际构造函数,只对其进行访问,并防止任何未经验证的行为如果您不想从构造函数中抛出异常,可以将构造函数设置为私有,并创建一个静态方法,返回对象的新实例,如果参数无效,则返回null但是,此方法必须检查结果是否为null

示例:

public class Foo {
  private Foo(int arg1, Bar arg2) {
    // guaranteed to be valid.
  }

  public static Foo construct(int arg1, Bar arg2) {
    // perform validation
    if (arg1 < 0 || arg2 == null) {
      return null;
    } else {
      return new Foo(arg1, arg2);
    }
  }
}

确保已将有效参数传递给构造函数的一种方法是,使用仅接受所需参数的构造函数创建父类,然后创建最终用户使用的子类并传入所需的参数,然后它们必须至少传入正确的数据对象。至于这些参数的有效值,则取决于您是否希望在父类构造函数中包含验证并引发运行时异常等等

下面是一个超类/子类的例子。让我们调用superclass
SomeShape
和子类
Triangle
。对于任何
SomeShape
对象,您将强制“用户”提供许多边和边长。这就是

public class SomeShape {
    private int numSides;
    private int sideLength;

    public SomeShape(int mNumSides, int mSideLength) {
        numSides = mNumSides;
        sideLength = mSideLength;
    }
}

public class Triangle extends SomeShape {
    private int height;

    public Triangle(int mNumSides, int mSideLength, int mHeight) {
        super(mNumSides, mSideLength);
        height = mHeight;
    }   
}

除了将大量逻辑和异常硬编码到构造函数中之外,这是一种相对干净的方法来强制执行创建对象所需的参数。

正如您和其他人所说,非法参数会破坏程序,对吗?@TiagoSirious“break”是什么意思?它将立即退出构造函数,并向调用方抛出异常。您希望这样做是因为
public class SomeShape {
    private int numSides;
    private int sideLength;

    public SomeShape(int mNumSides, int mSideLength) {
        numSides = mNumSides;
        sideLength = mSideLength;
    }
}

public class Triangle extends SomeShape {
    private int height;

    public Triangle(int mNumSides, int mSideLength, int mHeight) {
        super(mNumSides, mSideLength);
        height = mHeight;
    }   
}