常见的设计实践是使实例变量成为私有变量,并让公共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;
}
}
发布于 2013-08-03 15:40:56
你什么都没错过。你做什么完全取决于你的情况。然而,考虑到这一点:
在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,并且您正在设置的数据打破了基类假设的不变量,那么相关的setter应该是最终的,或者基类不应该做这些假设。如果重写设置器破坏了基类不变量,那么还有一个更大的问题要处理。。
在上面的示例中,您会注意到getter/setter是最终的。这是因为我们的规则是“任何示例都必须有0到10的值”。因此,此规则扩展到子类。如果我们没有该规则,并且如果一个示例可以具有任何值,那么我们就不需要最后一个setter,并且可以允许子类覆盖。
希望这能有所帮助。
发布于 2013-08-03 15:33:02
有时候,当您想要使类不可变时,这只是您需要做的事情之一。在这种情况下根本没有setter方法。
发布于 2013-08-03 15:37:27
根据上下文的不同,使用getter和setter实际上比在构造函数中使用成员变量更违反封装。如果您想设置这个类的成员变量“名称”,这两种方法都可以工作,因为构造是对调用者隐藏的,因此不会违反封装。一个警告是,在构造函数中使用setName可能会调用子类中过大的方法,这可能不是您想要的(因为它可能在超类中留下未定义的名称)。
这里有一个与你类似的问题,可以提供更多的洞察力:
https://stackoverflow.com/questions/18034360
复制相似问题