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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18034360/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 21:56:06  来源:igfitidea点击:

Java setting private fields inside constructors

javaconstructorprivate-members

提问by user373201

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?

常见的设计实践是将实例变量设为私有,并让公共 getter 和 setter 访问它们。但是很多时候我在 Internet 上看到代码示例具有将值直接分配给私有实例变量而不是在构造函数中使用 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;
    }
}

采纳答案by Jason C

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):

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

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:

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

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:

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

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).

现在,仍然存在您可能想要直接赋值的情况。一方面,也许你没有可用的 setter(尽管我认为创建私有或包私有 setter 仍然是可取的,出于上述原因,如果需要反射/bean 支持,以及在更复杂的代码中易于验证)。

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.

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

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.

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

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.

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

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.

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

Hope that helps.

希望有帮助。

回答by ataulm

Setting variables to private is to encourage encapsulation from other classes.

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

Unless setName(String)was meant to do something extra (which the method name doesn't imply), it's unnecessary to use the setter while you're in the class where the private variable is.

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

回答by giorashc

This does not defeat encapsulation since the private member is still hidden from the other classes

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

If the modifier method does not contain any logic and just sets the member then there is no difference between directly setting the member of calling its setter method although for better practice the setter should be called.

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

The setter indicates that this person's name might change in the future and allows it easily without creating an entire person object again.

setter 指示此人的姓名将来可能会更改,并且无需再次创建整个 person 对象即可轻松允许更改。

回答by NINCOMPOOP

Sometimes when you would want make the class immutable, it is just one of the things you need to do. And don't have setter methods at all in that case.

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

回答by sandiee

the private variables are accessible directly anywhere in the class

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

settng variabels private is to encapsulate them from other classes

settng variabels private 就是把它们从其他类中封装起来

回答by Benjamin Pack

Depending on the context, the use of getters and setters is actually a bigger violation of encapsulation than using member variables in constructors. If you want to set the member variable 'name' of this class, either of these approaches would work since the construction is hidden from the caller and thus not violating encapsulation. One warning is that the use of setName within the constructor might call an overrided method in a subclass which may not be what you want (since it may leave name undefined in the superclass).

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

Here's a similar question to yours that may provide additional insight:

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

calling setters from a constructor

从构造函数调用 setter

回答by Ankur Shanbhag

Initializing variables inside constructor is a very common practice. It can be used to assign values to variables based on which constructor user has called. You cannot write code based on assumption that the client code will invoke setter method to assign value to instance variables. It is always safe to assign default value to a variable when its object is created (i.e inside constructor).

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

There is a difference between initializing variables within constructor and setting it to different value as per requirement of the calling code(using setter method). Both have different purposes and different objectives.

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

回答by Girish K Gupta

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.

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

回答by venkat balabhadra

Its ok to directly assign values with in class provided setter doesn't do any other processing.

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

Basically setters/getters are used to provide restrictive access to private data such as returning copy of the data instead of reference of private object, validating data in getter etc..

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

Since the constructor is part of the object itself, and we are sure what we are doing is right, then its ok.

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

回答by Kevin Swan

My preferred approach (as described by Joshua Bloch in "Effective Java") is to make the constructor private, make the fields final (i.e., eliminate the setters entirely), and require clients to obtain instances either using the Builder Patternor Factory Method Pattern, which would take care of any necessary validation to protect invariants. Then the (private) constructor would simply directly assign the given parameters (which have already been validated by the Builder or Factory Method) to the appropriate fields, which are privateand final.

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