我最近开始了一个新项目,并且我试图将我的实例变量始终初始化为某个值,因此它们都不会在任何时候为null。下面的小例子:
public class ItemManager {
ItemMaster itemMaster;
List<ItemComponentManager> components;
ItemManager() {
itemMaster = new ItemMaster();
components = new ArrayList<ItemComponentManager>();
}
...
}
关键是主要是为了避免在代码中某处使用实例变量之前对 null 进行繁琐的检查。到目前为止,它工作得很好,你大多不需要 null - 值,因为你也可以检查空字符串或空列表等。我没有使用这种方法作为方法范围的变量它们的范围非常有限,因此不会影响代码的其他部分。
这一切都是实验性的,所以我想知道这种方法是否有效,或者是否有一些我还没有看到的陷阱。保持实例变量初始化通常是一个好主意吗?
答案 0 :(得分:9)
我通常将空集合和空集合视为两个独立的东西:
空集合意味着我知道零项目可用。 null集合将告诉我我不知道集合的状态,这是另一回事。
所以我真的不认为这是一个/或者。如果我在构造函数中初始化变量,我会声明变量 final 。如果你声明它是最终的,那么读者就会明白这个集合不能为空。
答案 1 :(得分:4)
首先,如果您想保留控制权,必须将所有非最终实例变量声明为私有!
考虑延迟实例化 - 这也避免了“坏状态”,但只在使用时初始化:
class Foo {
private List<X> stuff;
public void add(X x) {
if (stuff == null)
stuff = new ArrayList<X>();
stuff.add(x);
}
public List<X> getStuff() {
if (stuff == null)
return Collections.emptyList();
return Collections.unmodifiableList(stuff);
}
}
(注意使用Collections.unmodifiableList - 除非你真的希望调用者能够从你的列表中添加/删除,否则你应该使它不可变)
考虑将创建多少个有问题的对象实例。如果有很多,并且您总是创建列表(并且最终可能会有许多空列表),那么您可能会创建比您需要的更多的对象。
除此之外,这真的是一种品味问题,如果你在构建时能够拥有有意义的价值观。
如果您正在使用DI / IOC,您希望框架为您完成工作(尽管您可以通过构造函数注入来完成;我更喜欢setter) - 斯科特
答案 2 :(得分:3)
我会说完全没问题 - 只要你记得那里有“空”占位符值而不是真实数据。
将它们保持为null具有强制您处理它们的优点 - 否则程序崩溃。如果您创建空对象,但忘记它们,则会得到未定义的结果。
只是评论defencive编码 - 如果你是创建对象的人并且从不将它们设置为null,则不需要每次都检查null。如果由于某种原因你获得了空值,那么你知道某些事情已经发生了灾难性的错误,程序应该会崩溃。
答案 3 :(得分:2)
如果可能,我会让他们最终。然后,它们必须在构造函数中初始化,并且不能变为null。
您也应该在任何情况下将它们设为私有,以防止其他类为它们分配null。如果您可以检查类中是否从未分配过null,则该方法将起作用。
答案 4 :(得分:2)
我遇到过一些导致问题的案例。
在反序列化期间,一些框架不会调用构造函数,我不知道他们选择如何或为什么这样做但它发生了。这可能会导致您的值为null
。我也遇到过调用构造函数的情况,但由于某种原因,成员变量没有被初始化。
实际上我会使用以下代码而不是你的代码:
public class ItemManager {
ItemMaster itemMaster = new ItemMaster();
List<ItemComponentManager> components = new ArrayList<ItemComponentManager>();
ItemManager() {
...
}
...
}
答案 5 :(得分:2)
我处理我声明的任何变量的方式是决定它是否会在对象的生命周期内变化(如果它是静态的,则为类)。如果答案是“否”,那么我将其作为最终答案。
最后强制你在创建对象时给它一个值...我个人会做以下事情,除非我知道我会改变这一点:
private final ItemMaster itemMaster; private final List components; // instance initialization block - happens at construction time { itemMaster = new ItemMaster(); components = new ArrayList(); }
您的代码现在的方式您必须始终检查null,因为您没有将变量标记为私有(这意味着同一个包中的任何类都可以将值更改为null)。
答案 6 :(得分:1)
是的,在构造函数中初始化所有类变量是个好主意。
答案 7 :(得分:1)
重点是避免这一点 在使用之前检查null是否繁琐 一个类变量 代码。
您仍然需要检查null。第三方库甚至Java API有时会返回null。
此外,实例化一个可能永远不会被使用的对象是浪费,但这取决于你的类的设计。
答案 8 :(得分:1)
对象应在构造完成后100%准备好使用。用户不必检查空值。防御性编程是要走的路 - 保持检查。
为了DRY的利益,您可以将检查放入setter中,只需让构造函数调用它们即可。这样你就不会对支票进行两次编码。
答案 9 :(得分:0)
如果您在其中一种方法中设置了
,会发生什么itemMaster = null;
或者将ItemManager的引用返回给其他类,并将itemMaster设置为null。 (您可以防止这种情况轻松返回您的ItemManager的克隆等)
我会保留支票,因为这是可能的。
答案 10 :(得分:0)
如果它是您的所有代码并且您想要设置该约定,那么它应该是一件好事。不过,我同意保罗的评论,没有什么能阻止一些错误的代码意外地将你的一个类变量设置为null。作为一般规则,我总是检查null。是的,这是一个PITA,但防御性编码可能是一件好事。
答案 11 :(得分:0)
从类“ItemManager”的名称,ItemManager听起来像某个应用程序中的单例。如果是这样,你应该调查,真的,真的,知道依赖注入。使用Spring(http://www.springsource.org/)之类的东西来创建ItemComponentManagers并将其注入ItemManager。
没有DI,在严肃的应用程序中手动初始化是一个噩梦,调试和连接各种“管理器”类,使窄测试是地狱。
始终使用DI(即使在构建测试时)。对于数据对象,创建一个get()方法,如果该列表不存在,则创建该列表。但是,如果对象很复杂,几乎可以肯定会使用Factory或Builder模式更好地找到你的生活,并让F / B根据需要设置成员变量。