类的最佳实践

21
如果我有一个客户类,其中包含一个已重载的构造函数(默认构造函数和一个带参数的构造函数),那么在重载的构造函数中设置类成员变量的正确方法是什么?使用“this”引用还是使用setter方法?
我不确定哪种方法是正确的。
public class Customer {

private String firstName;
private String lastName;
private int age;

public Customer() {}

//This Way
public Customer(String firstName, String lastName, int age)
{
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
}

// Or this way?
  public Customer(String firstName, String lastName, int age)
{
    setFirstName(firstName); 
    setLastName(lastName);
    setAge(age);
}



/**
 * @return the firstName
 */
public String getFirstName() {
    return firstName;
}

/**
 * @param firstName the firstName to set
 */
public void setFirstName(String firstName) {
    this.firstName = firstName;
}

/**
 * @return the lastName
 */
public String getLastName() {
    return lastName;
}

/**
 * @param lastName the lastName to set
 */
public void setLastName(String lastName) {
    this.lastName = lastName;
}

/**
 * @return the age
 */
public int getAge() {
    return age;
}

/**
 * @param age the age to set
 */
public void setAge(int age) {
    this.age = age;
}

}

6个回答

27

第一种方法(使用this.)可能更加安全和直接。考虑如果未来的子类重写了setter方法,这可能导致非常意外的行为。

如果您的类是final,那么这就无关紧要,并且两种方法都可以使用。


4
到目前为止,这是唯一明智的答案。实际上,几个代码检查工具会警告从构造函数调用非 final 方法的原因正是因为这个问题。(如果类不是 final 的,但 setter 方法是 final 的,那也同样好)。 - Ted Hopp
@KumarVivekMitra - 我不喜欢你的回答,正如我对它的评论所指示的那样。 - Ted Hopp
@TedHopp,我不是在谈论构造函数是否不可变,而是在谈论类本身......我的回答中已经很清楚地写明了。 - Kumar Vivek Mitra
+1。更多相关内容和权威参考请访问https://dev59.com/mXA75IYBdhLWcg3wPmZ8#3404369。 - ruakh
谢谢Joe,我感激你提供的信息。 - scarpacci
显示剩余2条评论

2

重点不在于哪种方式更好,而是你想要什么......

- 如果你希望你的类是可变的,则使用 setters

- 如果你希望你的类是不可变的,则我认为使用 this 是更好的选择。

我认为,在一些场合中,使用 this 是恰当的,比如当你从 Web 服务器或其他来源接收到一些数据时,然后将它们以自定义类的实例的形式存储在集合中.....

例如:

  • 创建一个名为 Student 的类,

  • 当你向某个 Web 服务发出请求时,你会得到响应,例如 JSON ......

  • 解析它,然后创建一个 Student 实例并将其存储在集合中。

    例如:

    ArrayList<Student> arList = new ArrayList<Student>();

    arList.add(new Student(name,rollNos,class,marks));


从构造函数中调用非 final 方法(例如 setter)并不是一个好主意。请参见,例如,这篇文章 - Ted Hopp
@TedHopp 我认为你在谈论OCP原则,类应该是开放扩展的但是关闭修改的。而且我知道构造函数无法被继承,因此也无法被重载,所以它是不可变的。 - Kumar Vivek Mitra
点击此处了解为什么永远不应该从构造函数中调用可重写方法。除非方法或类是final的,或者方法是private的(在这种情况下方法无法被覆盖),否则大多数lint工具都会警告从构造函数中调用setter方法。 - Ted Hopp
@TedHopp,我认为我们之间有些误解...我不是在谈论从构造函数调用重写方法的问题,而是在构造函数中使用“this”调用具有不同参数的“其他构造函数”。 - Kumar Vivek Mitra
啊,我对此没有任何反对意见。但是,这与 OP 的问题无关。 - Ted Hopp
我只是把这个知识作为附加内容提供给了OP。 - Kumar Vivek Mitra

1
最好的答案是“取决于”。通常情况下,除非setter在设置值之前执行更像计算的操作,否则您不需要使用setter。但是,如果您的setter仅直接设置值,则this可能最适合您。相反,如果使用setter进行验证等操作,则使用this将会错过这些操作。

0

基本上,除了验证之外没有区别。

一方面,如果您的setter仅基于新值验证新值(它们不依赖于对象状态),则调用setter可以避免重复验证逻辑。

另一方面,如果一个setter的验证检查其他属性,则您必须确保在调用该setter之前已经设置了所需的属性,或直接分配属性。


0

直接访问方式更快(如果我没记错的话,会快7倍),但第二种方式在分配这些值或链接赋值时实现了一些特殊规则(其中你从另一个setter中分配第二个字段),除非当然你想在从构造函数执行分配时明确绕过这些规则/链。

所以简而言之,这取决于您的需求,但这些是我能想到的主要问题。


第二种方法并不比“更安全”,除非setter方法(或类)是final的。如果子类覆盖了setter并且没有调用超类,那会怎么样呢? - Ted Hopp
1
如果你正在子类化一个类型,并且重写了setter方法但没有对它们进行任何设置,那么你面临的问题比决定使用哪个构造函数要大得多。 - user1623834
@JordanWhite - 我认为你误读了问题。这里没有涉及“基类”问题。被设置的变量是类的私有变量。OP使用了“base”,而更合适的是“default”。 - Ted Hopp
@JordanWhite - 大多数lint工具会抱怨从构造函数调用非final方法的原因是因为这不是一种安全的做法。即使是理智的程序员有时也会犯错;在某个随机子类中缺少对super.setWhatever()的调用可能非常难以追踪(特别是如果症状只在执行期间较晚出现)。 - Ted Hopp
@TedHopp 啊,我并不是在主张任何一种方法,我只是简单地提到了覆盖setter的问题。个人而言,除非需要进行大量验证,否则我会直接设置变量。至于缺少对super的调用,可以通过一个简短的测试用例轻松验证。 - user1623834
显示剩余4条评论

0

正如已经提到的,构造函数中调用可重写方法非常危险。但是如果您需要执行某种在Setter方法中发生的验证,仍有一些合理的方法可以实现。

  1. 使Setter方法final,这是最快的解决方案。虽然已经提到了这个方法,但并不是唯一的选择。
  2. 使用Setter方法的私有副本
  3. 使用工厂方法代替构造函数。我通常坚持使用这种方式。因为这样,如果验证失败,您有更多自由处理情况,并且它比(2)更好地传达了意图。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接