Dav*_*ank 108 java null exception nullpointerexception custom-error-handling
Web服务返回一个巨大的XML,我需要访问它的深层嵌套字段.例如:
return wsObject.getFoo().getBar().getBaz().getInt()
Run Code Online (Sandbox Code Playgroud)
问题是getFoo()
,getBar()
,getBaz()
可能所有的回报null
.
但是,如果我null
在所有情况下检查,代码将变得非常冗长且难以阅读.此外,我可能会错过某些领域的支票.
if (wsObject.getFoo() == null) return -1;
if (wsObject.getFoo().getBar() == null) return -1;
// maybe also do something with wsObject.getFoo().getBar()
if (wsObject.getFoo().getBar().getBaz() == null) return -1;
return wsObject.getFoo().getBar().getBaz().getInt();
Run Code Online (Sandbox Code Playgroud)
写作是否可以接受
try {
return wsObject.getFoo().getBar().getBaz().getInt();
} catch (NullPointerException ignored) {
return -1;
}
Run Code Online (Sandbox Code Playgroud)
还是会被视为反模式?
Lii*_*Lii 135
捕捉NullPointerException
是一件非常棘手的事情,因为它们几乎可以在任何地方发生.很容易从一个bug中获取一个,偶然捕获并继续好像一切正常,从而隐藏一个真正的问题.处理它是如此棘手,所以最好完全避免.(例如,考虑一下null的自动拆箱Integer
.)
我建议你改用Optional
班级.当您想要处理存在或不存在的值时,这通常是最佳方法.
使用它你可以像这样写你的代码:
public Optional<Integer> m(Ws wsObject) {
return Optional.ofNullable(wsObject.getFoo()) // Here you get Optional.empty() if the Foo is null
.map(f -> f.getBar()) // Here you transform the optional or get empty if the Bar is null
.map(b -> b.getBaz())
.map(b -> b.getInt());
// Add this if you want to return an -1 int instead of an empty optional if any is null
// .orElse(-1);
// Or this if you want to throw an exception instead
// .orElseThrow(SomeApplicationException::new);
}
Run Code Online (Sandbox Code Playgroud)
使用Optional
s而不是null
可能缺少的值使得读者可以非常清楚地看到这一事实,类型系统将确保您不会意外忘记它.
您还可以更方便地访问处理此类值的方法,例如map
和orElse
.
但是也要考虑它是否是中间方法返回null的有效结果,或者是否是错误的标志.如果它始终是一个错误,那么抛出异常可能比返回一个特殊值,或者中间方法本身抛出异常更好.
另一方面,如果中间方法中缺少的值有效,那么也可以Optional
为它们切换到s?
然后你可以像这样使用它们:
public Optional<Integer> mo(Ws wsObject) {
return wsObject.getFoo()
.flatMap(f -> f.getBar())
.flatMap(b -> b.getBaz())
.flatMap(b -> b.getInt());
}
Run Code Online (Sandbox Code Playgroud)
我可以想到不使用的唯一原因Optional
是,如果这是代码中真正的性能关键部分,并且垃圾收集开销是一个问题.这是因为Optional
每次执行代码时都会分配一些对象,而VM 可能无法优化这些对象.在这种情况下,您的原始if测试可能会更好.
And*_*lko 12
我建议考虑一下Objects.requireNonNull(T obj, String message)
.您可以为每个异常构建带有详细消息的链,例如
requireNonNull(requireNonNull(requireNonNull(
wsObject, "wsObject is null")
.getFoo(), "getFoo() is null")
.getBar(), "getBar() is null");
Run Code Online (Sandbox Code Playgroud)
我建议你不要使用特殊的返回值,比如-1
.这不是Java风格.Java设计了异常机制,以避免使用来自C语言的这种老式方法.
投掷NullPointerException
也不是最好的选择.您可以提供自己的异常(检查以确保它将由用户处理或未经检查以更简单的方式处理它)或使用您正在使用的XML解析器的特定异常.
假设类结构确实不受我们的控制,就像在这种情况下一样,我认为按照问题中的建议捕捉NPE确实是一个合理的解决方案,除非性能是一个主要问题.一个小的改进可能是包装throw/catch逻辑以避免混乱:
static <T> T get(Supplier<T> supplier, T defaultValue) {
try {
return supplier.get();
} catch (NullPointerException e) {
return defaultValue;
}
}
Run Code Online (Sandbox Code Playgroud)
现在你可以简单地做:
return get(() -> wsObject.getFoo().getBar().getBaz().getInt(), -1);
Run Code Online (Sandbox Code Playgroud)
正如汤姆在评论中已经指出的那样,
以下声明违反了得墨忒耳法,
wsObject.getFoo().getBar().getBaz().getInt()
Run Code Online (Sandbox Code Playgroud)
你想要的是什么int
,你可以从中得到它Foo
.得墨忒耳法则说,永远不要和陌生人说话.对于您的情况,您可以隐藏在Foo
和的引擎下的实际实现Bar
.
现在,您可以创建方法Foo
来获取int
从Baz
.最终,Foo
将有Bar
在Bar
我们可以访问Int
不暴露Baz
直接Foo
.因此,空检查可能分为不同的类,并且只在类之间共享所需的属性.
归档时间: |
|
查看次数: |
13406 次 |
最近记录: |