设置方法或构造函数

Posted

技术标签:

【中文标题】设置方法或构造函数【英文标题】:Setter methods or constructors 【发布时间】:2013-10-21 23:28:23 【问题描述】:

到目前为止,我已经看到了两种在 Java 中设置变量值的方法。有时使用带参数的构造函数,其他的 setter 方法用于设置每个变量的值。

我知道一旦使用“new”关键字实例化类,构造函数就会在类中初始化实例变量。

但是我们什么时候使用构造函数,什么时候使用setter呢?

【问题讨论】:

那些不是变量,它们是(数据)成员。 【参考方案1】:

当您想要创建对象的新实例时,您应该使用构造函数方法,并且已经填充了值(即可以使用的对象,其中填充了值)。这样,您无需显式调用对象中每个字段的 setter 方法来填充它们。

当您想在对象创建后更改字段的值时,您可以使用 setter 方法设置值。

例如:-

MyObject obj1 = new MyObject("setSomeStringInMyObject"); // Constructor approach
// Yippy, I can just use my obj1, as the values are already populated
// But even after this I can change the value
obj1.setSomeString("IWantANewValue"); // Value changed using setter, if required.
..
MyObject obj2 = new MyObject();
obj2.setSomeString("setSomeStringNow"); // Setter approach
// values weren't populated - I had to do that. Sad :(

正如 Axel 提到的,如果你想创建不可变的对象,你不能使用 setter-methods 方法。我不会说所有东西都必须在构造函数中初始化,因为存在不同的方法,比如惰性求值,即使是不可变对象也可以使用

【讨论】:

+1 如果要创建不可变对象,则不能使用 setter 方法(我不会说所有内容都必须在构造函数中初始化,因为存在不同的方法,例如惰性求值可以甚至与不可变对象一起使用)。 我假设我们需要在类中有构造函数或 getter 和 setter。如果我错了,请告诉我我们需要同时使用两者的场景。 假设您的班级有 20 个奇怪的字段。该应用程序要求您快速创建该类的新对象,只需 5 个必填字段即可创建对象。有一个仅包含这 5 个字段的构造函数,并且对象实例化已完成。现在在应用程序的后面,有一个代码片段执行一些业务逻辑来设置/重置其他字段。在这种情况下,您将需要这些字段的设置器。这只是一个简单的案例,在很多情况下两者都可以结合使用。您对需求的评估应该是决定因素。 一般来说,我认为,你对字段的操作越多,你就越需要它的设置器。设置和忘记类型的字段通常作为构造函数参数。希望这会有所帮助。【参考方案2】:

我认为你问了一个很好的问题:- 但是我们什么时候使用构造函数,什么时候使用 setter?

首先,让我们从一些概念开始。我希望这个解释可以帮助每个想知道何时使用构造函数或 setters() 和 getters() 方法(访问器和突变器)的人。 构造函数类似于方法,但是构造函数方法 在 java 中:

1) 构造函数用于初始化对象的状态。 方法用于暴露对象的行为。

2) 构造函数不能有返回类型。 方法必须有返回类型。

3) Constructor 被隐式调用。 方法被显式调用。

4) Getters() 或访问器是提供对对象实例变量的访问的方法。 Setters() 或 mutators 是为调用者提供更新特定实例变量值的机会的方法。

清楚了这一点,让我们从面向对象编程 (OOP) 的角度来考虑,以满足 OOP 原则的要求(面向对象编程 (OOP) 是基于 四大原则构建的封装数据抽象多态Inheritance.)、Getter()Setter() 方法是实现这一点的关键.

这里有一个公式可以告诉你我的意思:

私有字段 + 公共访问器 == 封装;

当我们设置私有字段并使用公共访问器时,您可以根据此公式看到,我们正在执行 4 个 OOP 主体之一的封装。

在这里,我将为您提供两个类,我对其添加了注释以尝试使我的代码自我解释。将这些类作为CustomerTestCustomer [main() 方法] 类的方法的实验室,您可以复制代码并自己运行。请注意,我使用了两个构造函数来解释一个具有多个构造函数并具有公共 setters()getters() 方法以访问私有实例变量的类:

package com.exercise.lecture2;

/**
 * 1) Create a Customer class that has the following attributes:
 * name, SSN.
 * 2) This class should have two methods: getName() and getSSN().
 * 3) If the class is instantiated with only a SSN, then give the default name of "John Doe". (HINT: Use two constructors)
 * 4) Also, add a method toString(), that returns a string representation of the customer object (name and SSN concatenated).
 *      Make sure to set this method public.
 * 5) Create a class to test your program (e.g. a class that include the main() method). In your test program, instantiate
 *      three customers and print out the value using toString() method.
 * 
 * @author Samuel M.
 *
 */

//this class is complemented with class  TestLabCustomer.java
public class LabCustomer 

// Private filds: name and socialSecurityNum
    private String name;
    private int socialSecurityNum;

    // constructors
    public LabCustomer(String name, int socialSecurityNum) 
        this.name = name;
        this.socialSecurityNum = socialSecurityNum;
    

    /** The keyword 'this' can be used to call a constructor from a constructor,
     * when writing several constructor for a class, there are times when
     * you'd like to call one constructor from another to avoid duplicate code.
     */
    // Account with This() on a second constructor
    public LabCustomer(int socialSecurityNum) 
        this("John Doe", socialSecurityNum); // default name is printed if only the SSN is provided
    

    // Public accessors (getters and setters)
    String getName() 
        return name;
    

    void setName(String name) 
        this.name = name;
    

    int getSSN() 
        return socialSecurityNum;
    

    void setSSN(int socialSecurityNum) 
        this.socialSecurityNum = socialSecurityNum;
    


    // instance method
    public String toString()  //overriding the toString() method 
        return ("Customer name: " + getName() + ", SSN#: " + getSSN() ); // concatenating the name and SSN

    

这里是测试类,具有main() 方法并在实例化预览类的对象后调用实例方法:

package com.exercise.lecture2;

//this class is complemented with class  LabCustomer.java
public class TestLabCustomer 

    public static void main(String[] args) 
        // Instantiating an object of class LabCustomer and creating three customers objects
        LabCustomer cust1 = new LabCustomer("Juan Melendez", 123457789);
        LabCustomer cust2 = new LabCustomer("Mary Lee", 125997536);
        LabCustomer cust3 = new LabCustomer(124963574); // when instantiating with no "name", the default (John Doe) is printed 

        /**
         * Once you've instantiated an object and have an object variable,
         * you can use object variable to call an instance method. 
         *  e.g.:
         *  object variables: cust1, cust2, cust3
         *  call the method toString() using the object variable and dot [.] in order to perform the method call.
         */
        // calling method toString() in class LabCustomer to print customer values
        System.out.println(cust1.toString());
        System.out.println(cust2.toString());
        System.out.println(cust3.toString());

    


结果:

客户姓名:Juan Melendez,SSN#:123457789

客户姓名:Mary Lee,SSN#:125997536

客户姓名:John Doe,SSN#:124963574

【讨论】:

这是一个很好的例子,有没有我们可以指出执行数据抽象和多态的地方?我想在代码中看到这两个的可见性【参考方案3】:

简单来说:

如果您认为在使用对象之前必须进行初始化,请使用 构造函数

当变量的初始化不是强制使用对象时,使用 setter 方法。

有关详细信息,请参阅文档 page。

【讨论】:

【参考方案4】:

如果你想要一个不可变的类使用构造函数,否则使用setter。

【讨论】:

为什么?不可变类可以有设置器 @ZhekaKozlov 如果一个对象具有改变状态的设置器,那么根据定义该对象不是不可变的,对吧? @vancan1ty Setter 可以返回对象的新实例而不是对其进行变异 @ZhekaKozlov 我怀疑除非这在代码库上始终如一地应用,否则这将是一个混淆的秘诀(Java 习惯用法肯定是设置器(例如“setXXX”)修改对象)。我看到其他人出于您描述的目的使用“deriveXXX”或复制构造函数。【参考方案5】:

我们根据场景使用方法。

    构造方法:当对象的实例化必须有参数并且没有它们我们无法构造对象时,我们应该继续使用这种方法。

    Setter 方法:对于可选参数,我们可以继续使用 setter 方法。

【讨论】:

这对我来说更有意义,只是向某人解释【参考方案6】:

假设我们有一个名为 Counter 的类:

public class Counter
    int count;
    //constructor
    public Counter(int c)
        count = c;
    
    public void setCounter(int newCounter)
        count = newCounter;
    

在上面的类中,当你想创建一个 new Counter 对象时,你会使用一个构造函数并在其中设置 count 变量。像这样:

Counter myCounter = new Counter(1);

如果您想在运行时更改计数变量,您可以使用 setter 方法:

myCounter.setCounter(2);

【讨论】:

【参考方案7】:

在构造函数的情况下,当你更新字段时,你每次都使用 new 关键字创建一个新对象。

Customer customer = new Customer("Tom", 23, 10000);

例如,如果你想更新汤姆的工资,你需要用更新的工资重新写这行,这意味着内存浪费。

在设置方法的情况下,您只能在原始对象中设置单个字段。

【讨论】:

【参考方案8】:

在大多数情况下,我同时使用它们 :)(因为您有 9/10 次想要回来编辑某些内容(这不是创建新实例的好习惯)

我通常会做这样的事情

    public class User 

        private int id;
        private String initials;
        private String firstName;
        private String lastName;
        private String email;
    
        public User() 
            this(0, "", "", "");
        
    
        public User(int id, String firstName, String lastName, String email) 
            this.id = id;
            this.firstName = firstName;
            this.lastName = lastName;
            this.email = email;
        
       // Getters and setters should be here
    

当你想用它编辑某些东西时,你可以使用设置器,(例如,如果你将用户保存在 ArrayList 中,那么你可以从 ArrayList 中获取对象并设置字段你想编辑,而不是创建一个全新的对象:)

【讨论】:

“总是”?想想不可变的。【参考方案9】:

这取决于应用程序域和其中类的用途。

Java bean 通常有一个无参数构造函数和相关成员变量的 getter/setter。这种方法有很多优点,因为 Java bean 在许多框架(如 Struts 和 Spring)中都得到了开箱即用的支持。

一个类还可以通过将这些变量作为参数传递给基本构造函数来强制值的可用性。并且可以通过其他便利的构造函数或setter方法设置非强制值。

另一方面,不可变类可以具有构造函数,但需要修改其状态的缺失方法,包括设置器。

总体而言,可以在考虑应用程序的整体设计、其运行的框架1、必须执行的合同等情况下做出此类决定。

1 - 虽然它建议类设计应该独立于框架。像 Spring 这样的优秀框架不会强制执行此类要求。

【讨论】:

【参考方案10】:

您可以将这两种方法结合起来。在构造函数中,您可以调用实例变量的设置器。比如:

public class TestClass 
    private String someField;

    public TestClass(String someField) 
        setSomeField(someField);
    

    public String getSomeField() 
        return someField;
    

    public void setSomeField(String someField) 
        this.someField = someField;
     

【讨论】:

以上是关于设置方法或构造函数的主要内容,如果未能解决你的问题,请参考以下文章

通过构造函数或属性设置器进行依赖注入?

在 C# 中构造对象的首选方法是啥?构造函数参数或属性?

JavaScript设计模式之构造函数模式

在进行构造函数或设置器注入时,依赖项的正确粒度是多少?

对象的创建:构造函数或静态工厂方法

构造函数和原型