10

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

10 回答 10

8

You are not missing anything. What you do depends entirely on your situation. However, consider this:

It is very common to do parameter validation in a setter. For example, let's say I have a class with field that can hold a value 0 through 10 (the "throws" is unnecessary for the exception type below but I include it for clarity):

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

Here, setValue() validates 'value' to make sure it sticks to the rules. We have an invariant that states "an Example will not exist with an out of range value". Now let's say we want to make a constructor that takes a value. You might do this:

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

As you can see, there is a problem. The statement new Example(11) would succeed, and now an Example exists that breaks our rules. However, if we use the setter in the constructor, we can conveniently add all parameter validation to the constructor as well:

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

So there are many benefits to this.

Now, there are still cases when you might want to assign values directly. For one, maybe you don't have setters available (although I would argue that creating private or package private setters is still desirable, for the reasons mentioned above, for reflection/bean support if necessary, and for ease of validation in more complex code).

Another reason might be that perhaps you have a constructor that knows, somehow, ahead of time that valid values will be assigned, and therefore doesn't need validation and can assign variables directly. This is usually not a compelling reason to skip using setters though.

However, all-in-all, it's generally a good idea to use the setters everywhere when possible, it will usually lead to cleaner and clearer code that is easier to maintain as complexity increases.

Most of the examples you see where people set variables directly are just people being "lazy" - which is perfectly acceptable if the situation warrants it (perhaps you're writing a quick test program or application and don't want to implement a bunch of setters, for example). There's nothing wrong with that as long as you keep the big picture in mind and only be "lazy" when it's appropriate.

Something I'd like to add based on some of the other answers here: If you override a setter in a subclass, and the data you are setting breaks invariants that the base class assumes, then either the relevant setters should be made final or the base class should not make those assumptions. If overriding setters breaks base class invariants then there is a bigger issue at hand.

You'll notice the getter/setter is final in the above example. This is because our rule is that "any Example must have a value from 0 to 10". This rule therefore extends to subclasses. If we did not have that rule and if an Example could take on any value, then we would not need a final setter and could allow subclasses to override.

Hope that helps.

于 2013-08-03T15:40:56.140 回答
3

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

于 2013-08-03T15:33:02.480 回答
2

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

这是一个与您类似的问题,可能会提供额外的见解:

从构造函数调用 setter

于 2013-08-03T15:37:27.997 回答
1

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

settng variabels private 是从其他类中封装它们

于 2013-08-03T15:35:56.843 回答
0

可以直接使用类中提供的 setter 分配值,而不进行任何其他处理。

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

由于构造函数是对象本身的一部分,并且我们确信我们所做的事情是正确的,那么它就可以了。

于 2013-08-03T15:59:08.417 回答
0

将变量设置为私有是为了鼓励其他类的封装。

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

于 2013-08-03T15:32:30.403 回答
0

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

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

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

于 2013-08-03T15:32:37.193 回答
0

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

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

于 2013-08-03T15:37:29.237 回答
0

This is perfectly normal. Some variables might need to be initialized as soon as the object is created, hence it makes sense to pass them in the constructor and many times we may not want to provide setters for those variables to avoid changing the values after object is created.

于 2013-08-03T15:44:11.887 回答
0

我的首选方法(如 Joshua Bloch 在“Effective Java”中所描述的)是使构造函数私有,使字段成为最终的(即完全消除 setter),并要求客户端使用构建器模式工厂方法模式获取实例,它将负责任何必要的验证以保护不变量。然后(私有)构造函数将简单地将给定参数(已经由 Builder 或 Factory Method 验证)分配给适当的字段,即privatefinal

于 2018-08-08T16:21:13.767 回答