使用setter方法或直接引用变量内部构造函数?

时间:2014-08-14 19:47:12

标签: java

这两种方法都有效,但这是正确的方法吗?

方法一:

public class Object {
   private String name;

   public String getName() {
       return name;
   }

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

   public Object(String name){
       this.name = name;
   }
}

方法二:

public class Object {
   private String name;

   public String getName() {
       return name;
   }

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

   //Changed this.name = name to setName(name)
   public Object(String name){
       setName(name);
   }
}

我已经四处搜索,但无法找到提及此问题的确切问题。如果有,可以免费发布链接,我将删除问题

5 个答案:

答案 0 :(得分:6)

我的第一个想法是在构造函数中使用setter。因此,如果您想要更改名称的存储方式,或者如果您想在设置名称时添加任何其他行为,则只需更改一次。

但是考虑到这一点,我认为如果类不是 final 并且方法不是私有,那么使用对变量的直接访问会更好。否则有人可以扩展你的方法,覆盖方法,导致你的构造函数以不可预测的行为调用他们的方法。

经验法则: 如果该类不是final,则只应在构造函数中调用private方法。

答案 1 :(得分:4)

虽然在构造函数中使用setter可以减少代码重复,但是不鼓励在构造函数中调用可重写方法(即非final /非私有方法) - 在扩展类时可能会导致奇怪的错误。

考虑以下情况(基于您的示例):

public class Object {
   private String name;

   public String getName() {
       return name;
   }

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

   //Changed this.name = name to setName(name)
   public Object(String name){
       setName(name);
   }
}

使用以下子类:

public class SubObject extends Object {
   private String Id;

   @Override
   public void setName(String name) {
       super.setName(name + Id);
   }

   public SubObject(String name){
       super(name);
       this.id = "1";
   }
}

创建SubObject的实例将导致空指针,因为setName()在构造函数中被调用,但setName()的实现依赖于Id字段初始化。

扩展类的人不应该去检查超类的源代码,以确保构造函数不调用可重写的方法。

答案 2 :(得分:2)

我不会在构造函数中使用setter。这是因为如果有人在设置器中设置名称时添加了任何其他行为,我认为这是附带效果。

答案 3 :(得分:2)

如果所有的setter和构造函数都是一个简单的赋值,那么选择哪两种方式并不重要。

但是,如果在将新值分配给成员之前需要执行一些验证,那么将该逻辑放在一个地方是有意义的,这意味着从构造函数中调用setter方法是更好的选择。

答案 4 :(得分:1)

如果setName()有关于如何设置名称的内在逻辑,那么我会选择2.另一方面,如果setName()包含一些需要在名称时运行的附加代码设置,我会选择1。

让我做一些更复杂的情况,以便表达我的观点:

class Person {
    private String firstName;
    private String lastName;
    private boolean wasRenamed;

    //getters...

    public Person(String fullName) {
        ???
    }

    public void setFullName(String fullName) {
        ???
    }
}

这里我们有Person个名字和姓氏,我们也希望记录谁重命名,谁不记录。让我们说fullName包含以空格分隔的名和姓。现在让我们看一下您在问题中提供的两种不同方法:

  1. 在costructor中调用setFullName():这将导致代码重复(按空格分割fullName并将其分配给姓氏。
  2. 在costructor中调用setFullName():这会增加wasRenamed标志的额外麻烦,因为setFullName()必须设置此标志。 (这可以通过在调用false之后简单地将标志重置回构造函数中的setFullName()来解决,但是让我们说我们不想这样做)
  3. 所以我会使用 1和2的组合,并拆分设置名称的内部逻辑和在名称设置为不同方法之前/之后需要运行的其他代码: / p>

    class Person {
        private String firstName;
        private String lastName;
        private boolean wasRenamed;
    
        //getters...
    
        private void setFullName0(String fullName) {
            //split by space and set fields, don't touch wasRenamed flag
        }
    
        public Person(String fullName) {
            setFullName0(fullName);
        }
    
        public void setFullName(String fullName) {
            setFullName0(fullName);
            wasRenamed = true;
        }
    }