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

时间:2013-08-03 15:27:42

标签: 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;
    }
}

10 个答案:

答案 0 :(得分:6)

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

在setter中进行参数验证是很常见的。例如,假设我有一个带有字段的类,它可以保存0到10的值(下面的例外类型不需要“抛出”但是为了清楚起见我包括它):

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.");
    }
}

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

public class Example {
    ...
    public Example (int value) {
        this.value = value;
    }
    ...
}

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

public class Example {
    ...
    public Example (int value) throws IllegalArgumentException {
        setValue(value); // throws if out of range
    }
    ...
}

所以这有很多好处。

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

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

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

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

我想根据其他一些答案添加一些内容:如果覆盖子类中的setter,并且您设置的数据会破坏基类假定的不变量,那么应该制作相关的setter final或基类不应该做出这些假设。 如果重写的setter打破了基类不变量,那么手头就会出现更大的问题。

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

希望有所帮助。

答案 1 :(得分:2)

有时当你想让类不可变时,它只是你需要做的事情之一。在这种情况下根本没有setter方法。

答案 2 :(得分:2)

取决于上下文,使用getter和setter实际上比在构造函数中使用成员变量更大程度地违反封装。如果你想设置成员变量&#39; name&#39;在这个类中,这些方法中的任何一个都可以工作,因为构造对调用者是隐藏的,因此不会违反封装。一个警告是在构造函数中使用setName可能会调用子类中的覆盖方法,这可能不是您想要的(因为它可能在超类中保留未定义的名称)。

这里有一个类似的问题,可能会提供额外的见解:

calling setters from a constructor

答案 3 :(得分:0)

将变量设置为private是为了鼓励从其他类封装。

除非setName(String)意味着做一些额外的事情(方法名称并不暗示),否则当你在私有变量所在的类中时,不必使用setter。

答案 4 :(得分:0)

这不会破坏封装,因为私有成员仍然对其他类隐藏

如果修饰符方法不包含任何逻辑并只设置成员,那么直接设置调用其setter方法的成员之间没有区别,尽管为了更好的实践,应该调用setter。

setter表示此人的姓名将来可能会更改,并且可以轻松地更改,而无需再次创建整个人物对象。

答案 5 :(得分:0)

私有变量可以直接在类中的任何地方访问

设置变量private是从其他类中封装它们

答案 6 :(得分:0)

在构造函数中初始化变量是一种非常常见的做法。它可用于根据构造函数用户调用的值为变量赋值。您不能基于客户端代码将调用setter方法为实例变量赋值的假设来编写代码。在创建对象时(即在构造函数内部),将默认值赋给变量总是安全的。

在构造函数中初始化变量并根据调用代码的要求将其设置为不同的值(使用setter方法)之间存在差异。两者都有不同的目的和不同的目标。

答案 7 :(得分:0)

这是完全正常的。一旦创建了对象,就可能需要初始化一些变量,因此在构造函数中传递它们是有意义的,并且很多时候我们可能不希望为这些变量提供setter以避免在创建对象后更改值。

答案 8 :(得分:0)

可以直接在类中提供值,而不提供任何其他处理。

基本上,setter / getter用于提供对私有数据的限制性访问,例如返回数据副本而不是私有对象的引用,验证getter中的数据等。

由于构造函数是对象本身的一部分,我们确信我们正在做的是对的,然后就可以了。

答案 9 :(得分:0)

我首选的方法(如Joshua Bloch在“有效的Java”中所述)是将构造函数设为私有,将字段定为最终字段(即,完全消除设置方法),并要求客户端使用{{3 }}或Builder Pattern,它们会进行必要的验证以保护不变式。然后,(私有)构造函数将直接将给定的参数(已经由Builder或Factory方法验证过)直接分配给相应的字段,即privatefinal