有时我会面对我必须写一段这样的代码(通常它有更多的嵌套if和更复杂的结构,但是对于这个例子来说)
public void printIt(Object1 a){
if (a!=null){
SubObject b= a.getB();
if (b!=null){
SubObject2 c=b.getC();
if(c!=null){
c.print();
}
}
}
}
当我不需要知道什么是失败的,如果什么是空的什么都不做,一种方法可能是
public void printIt(Object1 a){
try{
a.getB().getC().print();
}catch (NullPointerException e) {
}
}
第二种形式是否存在问题,如表现或其他类型的问题?
答案 0 :(得分:20)
异常版本(类似于使用Groovy的安全导航操作符?.
的链)使得很容易采用得墨忒耳定律(或者我称之为Demeter的强烈建议)并将其作为玩具过夜。
同样,深层嵌套的if
- 语句导致难以阅读的代码,并且在其下面存在相同的“违规”,并且此类方法的圈复杂度很高。
public void printIt(Object1 a) {
if (null == a) {
return;
}
SubObject b = a.getB();
if (null == b) {
return;
}
SubObject2 c = b.getC();
if (null == c) {
return;
}
c.print();
}
我宁愿在适当的地方看到LAM(小辅助方法)封装检查,几乎完全消除了对问题的需求。
答案 1 :(得分:10)
是。第二个版本会有糟糕的表现。
不要将异常用于正常控制流程。有效的Java项目57:仅在特殊情况下使用例外。
== UPDATE ==
甚至忽略了性能问题(根据我的benchmark,异常比以前更快,但不如简单的检查那么快),使用标准程序流的异常就像代码一样臭这个。即使在if
语句中,JVM字节码也可以对空检查进行特殊优化。第一个代码示例是非常受欢迎的。
答案 2 :(得分:8)
public void printIt(Object1 a){
if(a==null){
throw new IllegalArgumentException("a was null, but this is not allowed here."),
}
[...]
快速失败并努力失败。如果shoud不为null,则抛出异常。这将使您的代码更加稳定和可靠。
因此,如果我必须在你的a)和你的b)之间做出决定,我会选择a)。但如果a不能为null,则会隐藏错误情况。
答案 3 :(得分:7)
第二个版本的最大部分是,当NPE发生在getB()
内,getC()
时,它会被默默地忽略。如前所述,例外情况适用于特殊情况。
答案 4 :(得分:2)
使用异常在性能方面总是一个坏主意,无论机制过去和现在的速度有多慢。每当抛出异常时,将展开完整堆栈以创建堆栈跟踪。因此,就像Lois Wasserman所说的那样,你不应该依赖它们来进行(常规)程序流程,而是针对特殊情况。
嵌套ifs不是美的定义,但会让你能够打印其他信息,比如'B is null'等。
答案 5 :(得分:2)
答案是使用版本A.
使用Exceptions进行流量控制通常被认为是“糟糕的设计”。例外情况是“特殊”,尤其是使用空检查完全可以避免的NPE。此外,使用空值检查,您可以告诉(即记录)哪个术语为空(您不会知道null与版本B的位置)。
请注意,性能 不再是抛出异常的问题(例如,只有在使用堆栈跟踪时才构建堆栈跟踪)。这是一个干净的代码问题。
但是,在某些情况下,使用流控制的异常是不可避免的,例如从SimpleDateFormat.parse()抛出的异常,因为在调用之前没有合理的方法来判断您的输入是不可解析的
答案 6 :(得分:2)
肯定是(a)但是你应该重新构造方法以避免嵌套前面答案中提到的if语句。异常不是它们曾经的性能损失,但仍然 比检查null慢,并且永远不应该像这样用于程序流控制。如果对象可以为null,则应检查它,但如果不允许,则应在分配对象引用的位置快速失败。在许多情况下,您可以使用默认实现(空列表是一个很好的示例)以完全避免空值,从而产生更清晰的代码。尽可能避免使用空值。
答案 7 :(得分:1)
代码永远不应包含未经检查的异常的异常处理程序。对于有可能为null的对象引用,应始终使用空检查。
答案 8 :(得分:0)
如果迁移到Java 8,则可以使用Optional和Lambdas。首先,您需要重写类以返回每种类型的Optional
:
class Object1 {
private SubObject b;
Optional<SubObject> getB() {
return Optional.ofNullable(b);
}
}
class SubObject {
private SubObject2 c;
Optional<SubObject2> getC() {
return Optional.ofNullable(c);
}
}
class SubObject2 {
@Override
public String toString() {
return "to be printed";
}
}
现在,您可以以简洁的方式链接调用而不会有NullPointerExceptions的风险:
a.getB()
.flatMap(SubObject::getC)
.ifPresent(System.out::println);
有关详细信息,请参阅Oracle的文章Tired of Null Pointer Exceptions? Consider Using Java SE 8's Optional!。
答案 9 :(得分:0)
使用Java 8可选:
Optional.ofNullable(a)
.map(Object1::getB)
.map(SubObject::getC)
.ifPresent(Object2::print);