Java在构造函数中设置私有字段 [英] Java setting private fields inside constructors

查看:263
本文介绍了Java在构造函数中设置私有字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通常的设计实践是使实例变量为私有的,并有公共getter和setter来访问它们。但是很多时候我已经看到了互联网上的代码示例,它们的构造函数直接将值赋给私有实例变量,而不是在构造函数中使用setter。我缺少一些东西?

  public class Person {
private String name;

public Person(String name){
//这是正确的,似乎整个封装的目的被击败
this.name = name;

//不应该使用
setName(name);
}

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

public void setName(String name){
this.name = name;
}
}


解决方案

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



在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范围。);
}
}

这里,setValue()验证'value'肯定它坚持规则。我们有一个不变量,说一个示例将不存在超出范围值。现在,让我们假设我们想要一个接受值的构造函数。您可以这样做:

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

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

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

因此,这有很多好处。



现在,还有一些情况,你可能想直接分配值。对于一个,也许你没有setters可用(虽然我认为创建私人或包私人设置仍然是可取的,由于上述原因,如果需要的反射/ bean支持,并且为了更容易验证更复杂的代码)。



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



但是,all-in-all,通常是一个好主意,在可能的地方使用setters通常会导致更清晰和更清晰的代码,随着复杂性的增加,它们更容易维护。



大多数例子中,人们直接设置变量只是lazy - 这是完全可以接受的,如果情况保证它(也许你正在写一个快速测试程序或应用程序,并且不想实现一堆安装程序,例如)。这是没有什么错,只要你保持大的想法,只是在适当的时候懒惰。



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



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



希望有帮助。


Common design practice is to make instance variables private and have public getters and setters to access them. But many times I have seen code samples on the internet that have constructors that assign values directly to the private instance variable instead of using the setters inside constructors. Am I missing something?

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;
    }
}

解决方案

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.

这篇关于Java在构造函数中设置私有字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆