我的问题如下,我有一个很长的Getter,即,
objectA.getObjectB().getObjectC().getObjectD().getObjectE().getName();
由于"坏"数据库/实体设计(有些事情比其他人晚一些介绍)getObjectB()
,getObjectC()
或getObjectD()
可能会返回NULL
。
通常我们会一直使用空值检查,但在这种情况下,我必须使用
ObjectB b = objectA.getObjectB();
if (b != null) {
ObjectC c = b.getObjectC();
if (c != null) {
ObjectD d = c.getObjectD();
if (d != null)
return d.getObjectE().getName();
}
}
return "";
相反,简单地使用try-catch块
会容易得多try {
return objectA.getObjectB().getObjectC().getObjectD().getObjectE().getName();
} catch (NullPointerException e) {
return "";
}
在这种情况下,我并不关心哪个对象返回NULL,它要么显示名称要么不要显示。是否有任何并发症或使用try-catch而不是检查是不好的设计?
感谢您的意见。
答案 0 :(得分:7)
如果是使用Java 8的选项,您可以使用Optional,如下所示:
Optional.ofNullable(objectA)
.map(a -> a.getObjectB())
.map(b -> b.getObjectC())
.map(c -> c.getObjectD())
.map(d -> d.getObjectE())
.map(e -> e.getName())
.orElse("");
答案 1 :(得分:4)
这种方法链被称为"火车残骸"并不是首选。 这样的陈述也违反了Law of Demeter。让我举一个 Robert C Martin 清洁代码一书的例子:
String scratchDirPath = ctxt.getOptions().getScratchDir().getAbsolutePath();
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(scratchDirPath));
//write to file...
这与你所拥有的类似,这是一种不好的做法。这至少可以重构到下面:
Options options = ctxt.getOptions();
File scratchDir = options.getScratchDir();
String scratchDirPath = scratchDir.getAbsolutePath();
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(scratchDirPath));
//write to file...
这仍违反了得墨忒耳法,但至少部分更好。 最好的方法是找出为什么需要scratchDirPath并请求ctxt对象提供给你。所以它看起来像 -
BufferedOutputStream bos = ctxt.createScratchDirFileStream();
这样ctxt不会暴露其所有内部。这将调用代码与ctxt的实现分离。
答案 2 :(得分:0)
这只是你和你的团队可以做出的判断,但是(至少)我会调出一个客观的事情:在其中一个方法返回null
的情况下,你的第一个代码块比第二个表现更好。相对于简单的分支,抛出异常是昂贵的。 (在没有方法返回null
的情况下,我认为第二个可能比第二个快得多,因为你避免了分支。)在任何一种情况下,如果性能很重要,在您的环境中测试它。 (但test it properly;微观基准很难。使用工具。)
大多数时候,差异在现实世界中并不重要,当然,这是运行时的重大差异。