我试图以一种我永远不会做的方式清理使用初始化bloks的类的代码,而我只是想知道我是否遗漏了一些信息。代码如下所示:
@Entity
class MyClass extends BaseClass {
@ManyToMany(fetch=FetchType.EAGER)
private Set<OherClass> others;
{
if (others == null)
others = new HashSet<OtherClass>();
}
public MyClass(){
super();
}
//getters, setters and other stuff follows
}
我认为没有理由更喜欢上述代码:
@Entity
class MyClass extends BaseClass {
@ManyToMany(fetch=FetchType.EAGER)
private Set<OherClass> others = new HashSet<OtherClass>();
}
或者这个:
@Entity
class MyClass extends BaseClass {
@ManyToMany(fetch=FetchType.EAGER)
private Set<OherClass> others;
public MyClass(){
this.others = new HashSet<OtherClass>();
}
}
我问过我的大学,但他唯一能回答的是初始化块是如何工作的以及我已经知道的其他事情。我想知道在序列化,反射,数据库持久性,注入或任何可能使代码变得必要的异常情况下,是否存在java(甚至已经修复过的旧版本)或框架(hibernate,spring)的一些微妙的不当行为。
答案 0 :(得分:5)
private Set<OherClass> others;
{
if (others == null)
others = new HashSet<OtherClass>();
}
上面的代码是在不了解Java语义的情况下编写的。 others == null
始终为true
。因此,这只是一种非常复杂和混乱的写作方式
private Set<OherClass> others = new HashSet<OtherClass>();
Hibernate确实会在对象构造周围包含一些“魔法”,但它仍然需要从默认构造函数中获取一个实例。此时所有实例初始化程序都已运行。
更一般地说,总是喜欢立即用常量表达式初始化集合值变量,更好的是,创建字段final
。对于其余的代码,这不用担心。
使用上面的内容,你仍然可以打开如何填充集合的所有选项,这可能因构造函数而异。
答案 1 :(得分:1)
我看到这个唯一有用的地方是,当others
的初始化需要多个步骤时,如果在BaseClass
的构造函数中调用了一个可能被子覆盖的方法-class,例如MyClass
,但您希望确保others
已正确初始化,因为此方法使用了它。听起来很复杂。我希望Java代码能够更加清晰:
public class BaseClass {
public BaseClass() {
init();
}
public void init() {
// do something...
}
}
public class MyClass extends BaseClass {
private Set<OtherClass> others;
{
others = new HashSet<OtherClass>();
others.add(new OtherClass("hallo"));
// or do more complicated stuff here
}
public MyClass() {
super(); // explicit or may also be implicit
}
@Override
public void init() {
// use initialized others!!
doSomething(this.others);
}
....
}
但是,这是一个非常非常糟糕的模式,您不应该在构造函数中调用非final或非私有方法,因为子类可能尚未正确初始化。
除此之外,others
如果没有初始化则始终为null,您不必对此进行测试。