从构造函数调用setter

什么是从build设者(如果有的话)调用一个增变器的pro和con,

即:

public MyConstructor(int x) { this.x = x; } 

与:

 public MyConstructor(int x) { setX(x); } public void setX(int x) { this.x = x; } 

你有偏好吗? (这不是家庭作业,只是看着我们的编码标准文档,它总是在构造函数中设置实例var的时候调用mutators,我不总是这样)

就个人而言,我会直接在大多数情况下设置variables。

方法通常期望实例在被调用的时候完全形成。 特别是,从构造函数中调用重写的方法是难以理解的代码和难以察觉的错误的秘诀。

话虽如此,我经常尝试使类不可变,在这种情况下,不仅没有setter,而且还必须从构造函数(或variables初始值设定项)设置最终variables:)

如果属性具有逻辑,setter逻辑通常是有效的,有时会将其传播给观察者。 我通常希望在方法开始时显式检查构造函数参数,并且在完全创build实例之前,您不希望发生任何更改传播。

我遵循两个关于构造函数的规则来最小化问题,这就是为什么我不使用mutator方法:

构造函数(非final类)只能调用final或private方法 。 如果你决定忽略这个规则,让构造函数调用非最终/非私有方法,那么:

  • 这些方法和他们可能调用的任何方法必须小心,不要假设实例已完全初始化
  • 覆盖这些方法的子类(可能甚至不知道超类构造函数调用那些方法的子类)不能假定子类的构造函数和超类的构造函数已被完全执行。 这个问题越来越糟糕的inheritance层次结构与“邪恶”构造函数的超类是。

那额外的认知包袱是否值得呢? 你可以允许一个简单的变异函数的exception,它只赋值给一个实例variables,因为没有什么好处,即使这似乎不值得。

[@Jon Skeet在他的回答中提到:“…尤其是,从构造函数中调用重载的方法是难以理解的代码和难以察觉的错误的秘诀。” 但我不认为这个问题的后果是足够强调的。 ]]

在实例完全初始化之前,构造函数应该小心地泄漏this 尽pipe前面的规则是关于类内部的方法和访问ivars的子类的,但在完全初始化之前,还必须小心(甚至是最终/私有)方法将其传递给其他类和实用程序函数。 构造函数调用的非私有的,可覆盖的方法越多,泄漏的风险就越大。


关于调用非最终非私有方法的构造函数的一些引用:

https://www.securecoding.cert.org/confluence/display/java/MET05-J.+Ensure+that+constructors+do+not+call+overridable+methods

http://www.javaworld.com/article/2074669/core-java/java-netbeans–overridable-method-call-in-constructor.html

http://www.javaspecialists.eu/archive/Issue210.html

我很less这样做,因此我从来没有遇到过问题。 它也可能产生意想不到的后果,特别是如果你的setter在子类中被覆盖,或者你的setter在初始化时触发了可能不合适的额外的方法调用。

这取决于你如何对待setters。 如果你认为这个类可以派生出来,你可以允许这样的调用,这样你就可以覆盖别处的行为。 否则,你可以把它设置为它。

当然,允许明确地调用setter也将提供在设置期间注入额外行为的方法(例如,如果需要应用安全性)。

除非你的setter正在做一些比this.x = x更复杂的事情,否则我会直接设置这个variables。 该参数直接映射到您的实例variables,而对我来说更有意义 – 揭示。

为什么我们的编码标准要求使用访问器(也可能是私有的),这有一个原因。 如果您很快想知道哪些代码正在更改某个字段,那么只需在Eclipse中调出setter的调用层次结构即可!

这是一个代码库中的一个巨大的节省时间,已经达到了200万行代码,并且还可以简化重构。

我不介意,我也没有强大的前提[和不坚持一些严格的标准],只是用你的判断。 但是,如果你去瓦特/ setter任何覆盖类的人必须意识到对象可能不完全创build。 这是说确保没有额外的代码在setter可以发布this引用。

Setter可以用于标准范围检查等,因此不需要额外的代码来validationinput。 再次使用setters是稍微肮脏的方法。

使用这个领域有明显的好处(比如确保子类的人不会恶意地改变行为),而且通常是首选的。 在图书馆。

其实c-tor是创build一个对象的3种可能的方式(序列化+克隆是另外两种),一些瞬态可能需要在c-tor之外处理,所以你仍然有什么需要思考的领域vs setter。 (编辑,还有另一半:不安全,但这是序列化使用)

我的首选是直接在构造函数中设置它们的原因有几个。 首先像this.x = x; 就像调用一个单独的方法来做同样的事情一样清楚。 其次,这个方法可能会被覆盖,除非它被标记为final,并且从构造函数调用潜在的重载方法在我的书中是一个很大的禁忌。 第三,大多数方法通常假定对象在执行时已经完成,而不是在构build中途完成。 在这种简单的情况下,这不应该引起任何问题,但在更复杂的情况下,这可能会导致严重的微妙的错误,需要很长的时间来追踪。

所有使用setter / getters的主要参数是,它意味着你可以通过改变它的名字来重命名这个字段,它的定义,getter / setter方法,以及所有的应该编译和罚款。 这个观点在我看来是无效的,因为任何体面的现代IDE都会用简单的键盘快捷键来重新命名这个领域的所有事件。

在构造函数中调用任何publicstaticnon-final方法取决于你,但最好的做法是永远不要在构造函数中调用这样的方法,因为这个方法可以在子类中重写,实际上只会调用这个方法的重写版本(If你使用多态行为)。

例如:

 public class Foo { public Foo() { doSmth(); // If you use polymorphic behavior this method will never be invoked } public void doSmth() { System.out.println("doSmth in super class"); } public static void main(String[] args) { new Bar(200); } } class Bar extends Foo { private int y;; public Bar(int y) { this.y = y; } @Override public void doSmth() { // This version will be invoked even before Barr object initialized System.out.println(y); } } 

它会打印0。

对于mo的细节,请阅读Bruce Eckel的“Thinking in Java”一章“多态性”