在构造函数中使用set方法来初始化类的字段是一个好习惯吗?

时间:2015-03-15 09:39:28

标签: java constructor mutators

我想在构造函数中使用类的set方法来检查要初始化的值,如果它们不符合我设置的约束则抛出异常。

代码示例

public class MyClass {

    // Fields
    private int number;
    private String string;

    // Constructor
    public MyClass(int number, String string) {
        setNumber(number);
        setString(string);
    }

    // Set Methods
    public void setNumber(int number) {
        if (number<=0) {    // Certain constrain for number
            throw new IllegalArgumentException("Number must be positive");
        }
        this.number = number;
    }

    public void setString(String string) { // Certain constrain for string
        if (string.equals("")) {
            throw new IllegalArgumentException("string cannot be empty");
        } 
        this.string = string;
    }

    public String toString() {
        return String.format("Ordered %d x %s%n", number, string);
    }

    public static void main(String[] args) {
        MyClass obj = new MyClass(8, "Souvlaki");   // Everything allright
        System.out.println(obj);
        try {
            MyClass obj2 = new MyClass(-3, "Mousaka");  // Error in number argument
        } catch (IllegalArgumentException exception) {  // catch the exception
            System.out.printf("Exception Caught: Number must be positive%n%n");
        }
        MyClass obj2 = new MyClass(4, "");  // Error in string argument
        // Allow the exception to end program execution
    }
}

输出

  

订购8 x Souvlaki

     

异常捕获:数字必须为正

     

线程中的异常&#34; main&#34; java.lang.IllegalArgumentException:string   在MyClass.setString(MyClass.java:23)at处不能为空   MyClass。(MyClass.java:10)在MyClass.main(MyClass.java:40)

输出正是我想要的。创建的第一个对象使用适当的值初始化。调用toString()方法隐式证明了这一点。 由于初始化错误,第二个和第三个对象抛出异常。 捕获第一个异常是为了允许程序继续执行。未捕获第二个异常是为了输出打印出的异常错误消息。

所以一切似乎都是正确的,但这是一个很好的编程技术还是隐藏了一些错误?

3 个答案:

答案 0 :(得分:4)

正如评论所示,可能存在问题。特别是,您可能需要查看What's wrong with overridable method calls in constructors?。底线大致是:有人可能会以意外的方式覆盖set...方法,并引用该类的其他(未初始化的)字段,这可能会导致各种错误。

专用验证方法可能是一种选择。但是,即使没有必要进行验证,也可以多次调用它们。

您可以通过set...方法final来缓解大部分问题。无论如何,这是一个很好的做法。正如Joshua Bloch在他的书“ Effective Java ”中所述,第17项:

“继承的设计和文档,否则禁止它”

这意味着您应该每个方法final,除非您明确要允许它被覆盖,文档应该如何覆盖它(或者,或者,使整个班级final)。

答案 1 :(得分:0)

您可以在类中创建一个checkInvariant()方法,而不是在构造函数中进行验证,该方法会验证所有字段。

class MyClass {
    private int num;
    private String value;

    public void checkInvariants() {
        assertNotEmpty(value, "String value cannot be empty");
        assertPositive(num, "Number num should be non-negative");
    }
}

然后在其他地方,您可以将此类的实例作为参数传递,首先调用此方法以确保不变量保持:

class SomeOtherClass {
    public void doSomethingWithMyClass(MyClass myClass) {
        myClass.checkInvariants();
        // Proceed with work.
    }
}

答案 2 :(得分:0)

您的变量可以在类中的任何位置访问,因此无需使用mutator方法来初始化变量。

如果要对输入参数进行一些验证,请使用另一种执行所需验证的方法。

在构造函数中调用验证方法。