我有一个MyClass类:
public class MyClass {
private MyComplexType member1;
}
我必须在member1
上做一些非常激烈的初始化。足够它很容易保证自己的方法,从MyClass
构造函数调用。
我的问题是,以下哪种格式最适合此方法?
private MyComplexType initMyComplexType() {
MyComplexType result = new MyComplexType();
// extensive initialization on result...
return result;
}
这样叫:
public MyClass() {
member1 = initMember1();
}
或
private void initMember1() {
member1 = new MyComplexType();
// extensive initialization on member1...
}
这样叫:
public MyClass() {
initMember1();
}
对于私有成员,哪种风格更好?为什么呢?
答案 0 :(得分:6)
我会选择第一个选项,因为它更清楚地表达了init方法的用途,并明确地显示了数据流。
更不用说它使init方法中的代码可以重用。如果您以后需要初始化另一个变量,您可以再次调用该方法而不必担心副作用。此外,如果该另一个变量位于另一个类中,您可以轻松地将该方法移动到两个位置都可访问的位置。
沿着这一行,我还会考虑将init方法保留为doExtensiveComplexCalculation
之类的东西,以将其与实际的成员变量分离。
答案 1 :(得分:4)
第二种方法的另一个缺点是字段member1可能会将部分初始化的MyComplexType暴露给另一个线程。
回复JörnHorstmann的重写受保护静态方法示例:
public class StaticOverrideParent {
protected static void doSomething() {
System.out.println("Parent doing something");
}
}
public class StaticNoOverride extends StaticOverrideParent {
public static void main(String[] args) {
doSomething();
}
}
public class StaticOverride extends StaticOverrideParent {
protected static void doSomething() {
System.out.println("Doing something");
}
public static void main(String[] args) {
doSomething();
}
}
运行StaticNoOverride会打印“Parent doing something”。 运行StaticOverride会打印“做某事”。
答案 2 :(得分:3)
选择选项1.除了彼得提到的理由之外,这是一种更好的做法,因为这样你有一个计算密集但无副作用的函数init()
,以及较轻但状态修改构造函数。将这两个特征分开是一种很好的做法。
此外,使用模板/工厂方法打开以进行扩展。在子类中更容易覆盖它(或者如果你使用模板方法,它的部分)。再次,这要归功于计算与状态修改的分离。
修改:正如其他人所说,也考虑将initComplexMember()
重命名为buildContextMember()
。
答案 3 :(得分:3)
只有第一个允许你将结果分配给最终成员,这对我来说足够了。
答案 4 :(得分:2)
已经给出了非常好的理由(最终成员分配,多线程问题,提高了可读性),技术性非常强对我来说足够了。我将只添加Java教程中的一些摘录:
Initializing Instance Members
通常,你会把代码放到 初始化一个实例变量 构造函数。那里有两个 使用构造函数的替代方法 初始化实例变量: 初始化程序块和最终方法。
例如,初始化程序块 变量看起来就像静态 初始化程序块,但没有 static keyword:
{ // whatever code is needed for initialization goes here }
Java编译器复制初始化程序 阻塞到每个构造函数中。 因此,可以使用这种方法 共享一段代码 多个构造函数。
无法覆盖最终方法 一个子类。这将在讨论中讨论 关于接口和继承的课程。 这是使用决赛的一个例子 初始化实例的方法 变量:
class Whatever { private varType myVar = initializeInstanceVariable(); protected final varType initializeInstanceVariable() { //initialization code goes here } }
如果,这尤其有用 子类可能希望重用 初始化方法。方法是 决赛因为非最终决定 实例初始化期间的方法 会引起问题。约书亚布洛赫 在中更详细地描述了这一点 Effective Java
我倾向于支持上述样式(除非我不希望在所有构造函数中进行初始化,但这很不寻常)。
答案 5 :(得分:1)
我更喜欢Spring IoC container到你所描述的那种复杂的硬编码初始化。它可以更好地分离关注点,是一个更好的单元测试环境。
答案 6 :(得分:1)
其他评论员已经给出了很好的理由来使用辅助函数来初始化变量。我只是想补充一点,我实际上更喜欢使用私有或受保护的静态功能。这非常清楚,这只是一个初始化助手,它不能修改对象的其他状态,也不能被子类覆盖。