Java在构造函数中设置私有字段

use*_*201 7 java constructor private-members

常见的设计实践是将实例变量设为私有,并让公共getter和setter访问它们.但很多时候,我在互联网上看到了具有构造函数的代码示例,这些构造函数将值直接分配给私有实例变量,而不是使用构造函数内部的setter.我错过了什么吗?

public class Person{
    private String name;

    public Person(String name){
        //is this right, seems like the whole encapsulation purpose is defeated
        this.name = name;

        //shouldn't this be used
        setName(name);
    }

    public String getName(){
        return this.name;
    }

    public void setName(String name){
        this.name = name;
    }
}
Run Code Online (Sandbox Code Playgroud)

Jas*_*n C 6

你没有遗漏任何东西.你做什么完全取决于你的情况.但是,考虑一下:

在setter中进行参数验证是很常见的.例如,假设我有一个带有字段的类,它可以保存0到10的值(对于下面的异常类型,"throws"是不必要的,但为了清楚起见我包括它):

public class Example {
    private int value; 
    public Example () {
    }
    public final int getValue () {
        return value;
    } 
    public final void setValue (int value) throws IllegalArgumentException { 
        if (value < 0 || value > 10)
            throw new IllegalArgumentException("Value is out of range.");
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,setValue()验证'value'以确保它符合规则.我们有一个不变量,表示"示例不存在超出范围值".现在让我们说我们想要创建一个带有值的构造函数.你可以这样做:

public class Example {
    ...
    public Example (int value) {
        this.value = value;
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

如您所见,存在问题.语句新示例(11)将成功,现在存在违反我们规则的示例.但是,如果我们在构造函数中使用setter,我们也可以方便地将所有参数验证添加到构造函数中:

public class Example {
    ...
    public Example (int value) throws IllegalArgumentException {
        setValue(value); // throws if out of range
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

所以这有很多好处.

现在,仍有一些情况需要您直接分配值.首先,也许你没有可用的setter(尽管我认为创建私有或包私有的setter仍然是可取的,出于上面提到的原因,如果需要的话,可以提供反射/ bean支持,并且为了便于在更复杂的代码中进行验证).

另一个原因可能是,您可能有一个构造函数,以某种方式提前知道将分配有效值,因此不需要验证并可以直接分配变量.这通常不是跳过使用setter的令人信服的理由.

但是,总而言之,在可能的情况下随处使用setter通常是一个好主意,它通常会导致更清晰,更清晰的代码,随着复杂性的增加更容易维护.

你看到人们直接设置变量的大多数例子只是人们"懒惰" - 如果情况允许的话,这是完全可以接受的(也许你正在写一个快速的测试程序或应用程序而不想实现一堆例如,setters).只要你牢记大局并且在适当的时候只是"懒惰",那就没有错了.

我想根据其他一些答案添加一些东西:如果你覆盖子类中的setter,并且你设置的数据打破了基类假设的不变量,那么相关的setter应该是final或者基类不应该做出这些假设.如果重写setter打破了基类不变量,那么就会出现更大的问题.

你会注意到getter/setter在上面的例子中是最终的.这是因为我们的规则是"任何示例必须具有0到10之间的值".因此,此规则扩展到子类.如果我们没有该规则并且示例可以采用任何值,那么我们就不需要最终的setter并且可以允许子类覆盖.

希望有所帮助.