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

常见的设计实践是将实例变量设为私有,并让公共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; } } 

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

在setter中进行参数validation是很常见的。 例如,假设我有一个带有字段的类,它可以保存0到10的值(下面的exception类型不需要“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."); } } 

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

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

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

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

所以这有很多好处。

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

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

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

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

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

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

希望有所帮助。

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

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

以下是与您相似的问题,可能会提供更多见解:

从构造函数中调用setter

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

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

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

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

设置器指示此人的名字将来可能会更改,并且可以轻松地允许它,而无需再次创建整个人物对象。

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

设置变量私有是将它们封装在其他类中

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

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

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

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

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

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

我的首选方法(如Joshua Bloch在“Effective Java”中所述)是使构造函数成为私有,使字段最终(即完全消除setter),并要求客户端使用Builder Pattern或Factory Method Pattern获取实例,它将负责任何必要的validation,以保护不变量。 然后(私有)构造函数将直接将给定参数(已经由Builder或Factory方法validation)分配给相应的字段,这些字段是privatefinal