java在从getter返回之前检查null是一种不好的做法吗?
我想防止getter返回空值,比如下面的示例。这种做法不好吗
例1
public Integer getMinutes() {
if (minutes == null)
minutes = 0;
return minutes;
}
例2
public List getTasks() {
if (tasks == null)
tasks = new ArrayList();
return tasks;
}
# 1 楼答案
简单的回答是:这取决于
更具体地说,在没有看到整个课堂并理解其设计的情况下,这个问题不可能以一般的方式回答。空检查是一个实现细节,在某些设计中是合适的,但在其他设计中可能是一种气味
一般来说,方法必须遵守它们的契约,仅此而已。因此,从API设计的角度来看,这个问题无关紧要。您可以按照一长串现有的最佳实践来设计API,然后根据需要设计实现。其中一个最佳设计实践是,在返回
Collection
时,不应将“零元素”特例化为null
。也就是说,如果你的方法可以合理地返回一个空集合,它应该返回一个空集合,而不是像null
这样的特殊值。这几乎总是简化了客户端代码,并删除了一整类潜在的NPE。这并不意味着您不能在内部使用null
来标记“零元素”,但这意味着如果您这样做,您应该在返回时执行转换(如示例中所示)因此,如果
getTasks()
方法指定它总是返回一个(可能是空的)列表,那么在示例中使用空检查是一个合理的实现选择。在您的具体示例中,它可能不是最佳选择,因为JDK提供了Collections.emptyList<T>()
之类的方法,它允许您以低廉的成本重新使用单例空列表实例,因此您不会在内存中保存任何内容,而且CPU的节省值得怀疑此外,通过使用“
null
表示空列表”约定,您要求所有内部方法要么必须检查tasks == null
,要么使用gettask()方法。在集合对象的特定案例中,我通常更喜欢使用Collections.empty*
方法而不是null来表示空列表2。类似的推理适用于Integer
情况,因为Integer.valueOf(0)
在all compliant JVMs上返回一个单例对象撇开这些具体情况不谈,当然有理由使用空检查来表示缺少的或默认的元素,尤其是在没有好的“空”元素出现或性能很重要的情况下。您会发现这种模式在设计良好的库(如Java运行时和Guava)中使用得比较频繁。所以你不能说这是个坏习惯。这是一种需要仔细评估的实践
1这些
Collection.empty*
方法的使用也适用于您的示例。在空的情况下,可以考虑直接返回^ {CD13}},而不是按需创建新的^ {< CD14}}。这是否合适取决于整个类设计,例如,该列表是否可变2是否
Collections.emptyList()
比显式空检查快实际上是一个复杂的问题。如果空列表非常罕见,以至于它们通常不会在给定的过程中出现,那么emptyList()
方法可能会更快,因为随着空列表的频率变为零,它的成本(获取者wrt)也会变为零。另一方面,当空列表出现时,它可能会为所有使用List
的方法创建一个多态调用站点:每个这样的站点可能会看到一个特殊的“空列表”实现和ArrayList
。这可能会降低代码的速度,尽管这在很大程度上取决于内联决策。因此,与通常的性能考虑一样,确切的答案是复杂的,如果细节重要,您需要分析# 2 楼答案
一般来说,是的,这是不好的做法
例如1,如果对象的值(在本例中是一个整数)真的是
null
,那么API用户会期望返回null
。不是0
。例如,您的API的用户可能将0
视为花费0分钟的操作,而null
可能被解释为意味着从未发生过任何事情。)例如,API用户不会期望getter产生这样的副作用,比如构建一个新列表
除非在方法的契约(javadoc)中包含这些行为,否则用户无法意识到实际发生了什么,这可能会导致他们使用不正确的数据。相反,他们应该预测
null
可以来自这些方法,并自行检查但是,您可以通过使用
javax.annotation.Nullable
和javax.annotation.Nonnull
等注释来帮助用户(及其ide),分别提示返回值可以为null,也不能为null。通过这种方式,他们可以智能地预测并响应方法结果的无效能力警告:正如罗伯特在回答中所说,在某些情况下,延迟加载是有意义的。但必须谨慎行事,而且只能在有意义的情况下进行。Joshua Bloch在他的书《有效的Java,第二版》中写了一个关于惰性初始化的子章节,他在书中解释说,惰性初始化应该“明智地”执行,并有效地称之为预优化