为什么从构造函数调用Set方法不是一个好主意?

时间:2013-11-21 09:10:19

标签: java inheritance

仅在继承或大多数情况下是真的吗?

public class MyClass {
   public int id;

     public MyClass() {
         // Some stuff 
         setId(5);
     }

     public setId(int id) {
         this.id = id;
     }
}

6 个答案:

答案 0 :(得分:6)

这是真的。

因为setter始终是public方法。如果你上课不是final,那么就会出现外来方法调用的问题。这不是线程安全的,即它被称为this引用的转义。因此,如果您正在调用方法,则从构造函数中应该是finalprivate。否则,safe initialization对象将不会发生,这会导致实际系统中出现许多错误。

除了上述内容之外,我们永远不应该从public调用constructor方法,因为如果该类用于继承,则构造函数不能直接或间接调用可覆盖的方法

如果违反此规则,将导致程序失败。超类构造函数在子类构造函数之前运行,因此在子类构造函数运行之前将调用子类中的重写方法。如果重写方法依赖于子类构造函数执行的任何初始化,则该方法将不会按预期运行。

source

答案 1 :(得分:3)

您不应该从构造函数中调用可覆盖的方法。

如果调用一个可以被costructor中的子类覆盖的方法,那么子类可能会访问尚未初始化的超类的变量。

例如,以下代码到目前为止看起来不错

class MyClass {
    public int id;
    protected String someStr;

    public MyClass() {
        SetId(5);
        someStr = "test";
    }

    public void SetId(int Id) {
        id = Id;
    }
}

如果您现在继承MyClass并覆盖SetId方法,则可能会访问尚未初始化的超类的someStr变量,因此会导致NullPointerException情况下。

class MySubClass extends MyClass {

    public void SetId(int Id) {
       id = Id;
       someStr.toString(); // will cause NullPointerException
    }
}

如果有更大的继承层次结构,可能很难看出NPE的原因。

答案 2 :(得分:3)

如果它只是你想要的setter方法,那么从构造函数本身初始化你的变量。

public class MyClass {
    int id; 
    public MyClass() { 
        id=5;   
    }
}

否则,您可以调用私有/最终方法来遵守OOP理论。

请记住,只有在构造函数中执行最后一个语句或退出构造函数后才能完成对象初始化。

[更新]

这里正在进行类似,更有趣的讨论

Java - Subclass calls supers constructor which calls subclass method instead of its own

答案 3 :(得分:2)

构造函数构造对象,您应该只调用您知道在“未完全构造的状态”中工作的东西。在你的例子中,SetId没有做任何其他设置值的事情,所以没关系。但是,如果SetId使用了尚未准备好的其他状态/信息,则可能会遇到问题。

这不是诫命 - 而是“对你做的事情保持警惕”

答案 4 :(得分:1)

这可能不是一个好主意。如果你没有让那个类成为final,并且不使setName(...)方法成为私有或最终的,那么其他人就可以扩展你的类并覆盖setName(...)方法。您的构造函数(在您的基类中)将在扩展类而不是您的实现中调用该方法。没有人知道该方法可以做什么。根据经验:构造函数不应该调用可以覆盖的方法

答案 5 :(得分:0)

由于在Java中,默认情况下所有函数都是虚函数,因此this thread中解释的原因会导致ctor中未密封的方法调用存在风险。

这就是为什么你可能想要制作SetId或MyClass final或私有。