我想防止getter返回null值,如下面的示例所示。这是一种不好的做法吗?
示例1
public Integer getMinutes() {
if (minutes == null)
minutes = 0;
return minutes;
}
示例2
public List getTasks() {
if (tasks == null)
tasks = new ArrayList();
return tasks;
}
答案 0 :(得分:3)
一般来说,是,这是不好的做法。
例如,如果对象的值(在本例中为Integer)真的是null
,则API用户希望返回null
。不是0
。例如,您的API的用户可能会将0
视为0分钟的操作,而null
可能被视为意味着某些事情从未发生过。)
例如,API用户不希望getter产生这样的副作用,例如构建新列表。
除非您在方法的合同(javadoc)中包含这些行为,否则不能期望用户意识到实际发生了什么,并且这可能导致他们使用不正确的数据。相反,他们应该预期null
可以来自这些方法,并自行检查。
但是,您可以通过使用javax.annotation.Nullable
和javax.annotation.Nonnull
等注释向用户(及其IDE)提示返回值分别为null或不为null,从而帮助他们。通过这种方式,他们可以智能地预测并对方法结果的无效能力做出反应。
警告:正如罗伯特在他的回答中所说,有些情况下延迟加载是有道理的。但必须小心谨慎,并且只有在有意义的情况下才能这样做。 Joshua Bloch在他的书“Effective Java,Second Edition”中写了一篇关于Lazy Initialization的子章节,他解释说Lazy Initialization应该“明智地”执行并有效地称之为预优化。
答案 1 :(得分:2)
您所描述的是一种名为Lazy Loading(https://en.wikipedia.org/wiki/Lazy_loading)的设计模式。如果在某些情况下使用它是一种很好的做法,但它不是一种治愈方法,或者仅仅是为了它的乐趣而使用它。
答案 2 :(得分:2)
简短的回答是: 取决于 。
更具体地说,如果没有看到整个班级并理解其设计,那么问题就无法以一般方式回答。空检查是一个实现细节,适用于某些设计,但在其他设计中可能会有异味。
一般来说,方法必须符合他们的合同,不再是。因此,从API设计的角度来看,这个问题是无关紧要的。您可以根据一长串现有最佳实践设计API,然后设计适合的实现。其中一个设计最佳实践是,在返回null
时,您不应将特殊情况“零元素”放入Collection
。也就是说,如果您的方法可以合理地返回一个空集合,它应该返回一个空集合,而不是像null
这样的特殊值。这几乎总是简化客户端代码并删除整类潜在的NPE。这并不意味着你不能在内部使用null
标记“零元素”,但它暗示如果你这样做,你应该在返回时执行转换(如在你的例子中)。
因此,如果您的getTasks()
方法指定它始终返回一个(可能为空)列表,则在示例中使用null检查是一种合理的实现选择。在您的具体示例中,它可能不是最佳选择,因为JDK提供了类似Collections.emptyList<T>()
的方法,它允许您廉价地重复使用单例空列表实例,因此您不会在内存和CPU中保存任何内容节约是值得怀疑的 1 。
此外,通过使用“null
表示空列表”约定,您需要所有内部方法都必须检查tasks == null
,否则使用getTasks()方法。在集合对象的特定情况中,我通常更喜欢使用Collections.empty*
方法而不是null来表示空列表 2 。类似的推理适用于Integer
案例,因为Integer.valueOf(0)
会在all compliant JVMs上返回单个对象。
除了这些特定情况之外,当然有理由使用空值检查来表示缺失或默认元素,特别是当没有好的“空”元素出现或性能很重要时。您会发现这种模式在设计良好的库(例如Java运行时和Guava)中经常使用。所以你不能说这是一个不好的做法。这是的做法,需要仔细评估。
1 这些Collection.empty*
方法的使用也适用于您的示例。在您的空案例中,您可以考虑直接返回Collections.emptyList()
,而不是按需创建新的ArrayList
。这是否合适取决于整体类设计,例如,该列表是否可变。
2 Collections.emptyList()
是否比显式空检查更快实际上是一个复杂的问题。如果空列表非常罕见以至于它们通常不会在给定进程中发生,则emptyList()
方法可能更快,因为当空列表的频率变为零时,其成本(与获取者相关)变为零。另一方面,当空列表确实发生时,它可以为使用List
的所有方法创建一个多态调用站点:每个这样的站点都可以看到一个特殊的“空列表”实现和ArrayList
。这可能会减慢代码速度,但很大程度上取决于内联决策。因此,与通常的性能考虑因素一样,确切的答案很复杂,您需要分析细节是否重要。