Java 空检查链与捕获NullPointerException
web服务返回一个巨大的XML,我需要访问它的深层嵌套字段。例如:Java 空检查链与捕获NullPointerException,java,exception,nullpointerexception,null,custom-error-handling,Java,Exception,Nullpointerexception,Null,Custom Error Handling,web服务返回一个巨大的XML,我需要访问它的深层嵌套字段。例如: return wsObject.getFoo().getBar().getBaz().getInt() public static class MyFoo { private int barBazInt; public MyFoo(Foo foo) { this.barBazInt = parseBarBazInt(); } public int getBarBazInt(
return wsObject.getFoo().getBar().getBaz().getInt()
public static class MyFoo {
private int barBazInt;
public MyFoo(Foo foo) {
this.barBazInt = parseBarBazInt();
}
public int getBarBazInt() {
return barBazInt;
}
private int parseFooBarBazInt(Foo foo) {
if (foo() == null) return -1;
if (foo().getBar() == null) return -1;
if (foo().getBar().getBaz() == null) return -1;
return foo().getBar().getBaz().getInt();
}
}
问题是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();
可以写吗
try {
return wsObject.getFoo().getBar().getBaz().getInt();
} catch (NullPointerException ignored) {
return -1;
}
或者这会被视为反模式吗?NullPointerException是一个运行时异常,因此一般不建议捕获它,而是避免它 无论您想在哪里调用该方法,都必须捕获异常(否则它将向上传播)。然而,如果在您的例子中,您可以继续使用值为-1的结果,并且您确信它不会传播,因为您没有使用任何可能为null的“片段”,那么我认为捕获它是正确的 编辑:
我同意@xenteros后面的说法,最好启动您自己的异常,而不是返回-1。例如,您可以将其称为
InvalidXMLException
。创建您自己的异常是值得考虑的。我们称之为MyOperationFailedException。您可以抛出它,而不是返回一个值。结果将是相同的-您将退出该函数,但不会返回硬编码值-1,这是Java反模式。在Java中,我们使用异常
try {
return wsObject.getFoo().getBar().getBaz().getInt();
} catch (NullPointerException ignored) {
throw new MyOperationFailedException();
}
编辑:
根据评论中的讨论,让我补充一下我以前的想法。在这段代码中有两种可能。一个是您接受null,另一个是,这是一个错误
如果这是一个错误并且发生了,那么当断点不够时,您可以使用其他结构来调试代码
如果可以接受,您就不关心这个空值出现在哪里。如果你这样做了,你肯定不应该把这些请求链起来。如果效率是个问题,那么就应该考虑“捕获”选项。 如果“catch”无法使用,因为它会传播(如“SCouto”所述),那么使用局部变量避免多次调用方法
getFoo()
,getBar()
和getBaz()
,,我建议考虑。您可以为每个异常构建包含详细消息的链,如
requireNonNull(requireNonNull(requireNonNull(
wsObject, "wsObject is null")
.getFoo(), "getFoo() is null")
.getBar(), "getBar() is null");
我建议您不要使用特殊的返回值,如-1
。这不是Java风格。Java设计了异常机制来避免这种来自C语言的老式方法
抛出
NullPointerException
也不是最好的选择。您可以提供自己的异常(选中它以保证用户可以处理它,或者不选中它以更简单的方式处理它),或者使用您正在使用的XML解析器中的特定异常。给出的答案似乎与其他所有异常都不同
我建议您在if
s中检查NULL
原因:
我们不应该给我们的程序留下任何崩溃的机会。
空指针由系统生成系统的行为
无法预测生成的异常。你不应该离开家
当您已经有了一种处理方法时,程序就掌握在系统手中
这是你自己的。并将异常处理机制用于额外的安全性
为了使代码易于阅读,请尝试以下检查条件:
if (wsObject.getFoo() == null || wsObject.getFoo().getBar() == null || wsObject.getFoo().getBar().getBaz() == null)
return -1;
else
return wsObject.getFoo().getBar().getBaz().getInt();
编辑:
这里需要存储这些值wsObject.getFoo()
,
中的wsObject.getFoo().getBar(),wsObject.getFoo().getBar().getBaz()
一些变量。我不这样做,因为我不知道回报
功能的类型
如有任何建议,我们将不胜感激 我的答案与@janki的答案几乎相同,但我想稍微修改一下代码片段,如下所示:
if (wsObject.getFoo() != null && wsObject.getFoo().getBar() != null && wsObject.getFoo().getBar().getBaz() != null)
return wsObject.getFoo().getBar().getBaz().getInt();
else
return something or throw exception;
如果对象有可能为空,您也可以为
wsObject
添加空检查。不要捕获NullPointerException
。你不知道它是从哪里来的(我知道这在你的情况下是不可能的,但可能是别的什么东西扔的),而且速度很慢。
您希望访问指定的字段,为此,每个其他字段都必须不为null。这是检查每个字段的完美理由。我可能会在一个if中检查它,然后创建一个可读性方法。正如其他人指出的那样,返回-1是一个非常古老的学校,但我不知道你是否有理由这样做(例如,与另一个系统交谈)
编辑:如果它违反了德米特定律,这是有争议的,因为WsObject可能只是一个数据结构(检查)。捕获
空点异常
是一件非常有问题的事情,因为它们几乎可以在任何地方发生。很容易从一个bug那里得到一个,意外地捕捉到它,然后像一切正常一样继续,从而隐藏了一个真正的问题这是一个非常棘手的问题,因此最好完全避免。(例如,考虑自动取消对空整数的装箱)
我建议你改用这门课。当您想要处理存在或不存在的值时,这通常是最好的方法
使用它,您可以编写如下代码:
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 null instead of an empty optional if any is null
// .orElse(null);
// Or this if you want to throw an exception instead
// .orElseThrow(SomeApplicationException::new);
}
public Optional<Integer> mo(Ws wsObject) {
return wsObject.getFoo()
.flatMap(f -> f.getBar())
.flatMap(b -> b.getBaz())
.flatMap(b -> b.getInt());
}
为什么不是可选的?
我能想到的不使用Optional
的唯一原因是,这是否在代码的真正性能关键部分,以及垃圾收集开销是否是一个问题。这是因为每次执行代码时都会分配一些可选的对象,VM可能无法优化这些对象。在这种情况下,您的原始if测试可能会更好。正如评论中所指出的
以下声明违反了
您需要的是int
an
wsObject.getFoo().getBar().getBaz().getInt()
Foo theFoo;
Bar theBar;
Baz theBaz;
theFoo = wsObject.getFoo();
if ( theFoo == null ) {
// Exit.
}
theBar = theFoo.getBar();
if ( theBar == null ) {
// Exit.
}
theBaz = theBar.getBaz();
if ( theBaz == null ) {
// Exit.
}
return theBaz.getInt();
public class JavaApplication14
{
static class Baz
{
private final int _int;
public Baz(int value){ _int = value; }
public int getInt(){ return _int; }
}
static class Bar
{
private final Baz _baz;
public Bar(Baz baz){ _baz = baz; }
public Baz getBar(){ return _baz; }
}
static class Foo
{
private final Bar _bar;
public Foo(Bar bar){ _bar = bar; }
public Bar getBar(){ return _bar; }
}
static class WSObject
{
private final Foo _foo;
public WSObject(Foo foo){ _foo = foo; }
public Foo getFoo(){ return _foo; }
}
interface Getter<T, R>
{
R get(T value);
}
static class GetterResult<R>
{
public R result;
public int lastIndex;
}
/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
WSObject wsObject = new WSObject(new Foo(new Bar(new Baz(241))));
WSObject wsObjectNull = new WSObject(new Foo(null));
GetterResult<Integer> intResult
= getterChain(wsObject, WSObject::getFoo, Foo::getBar, Bar::getBar, Baz::getInt);
GetterResult<Integer> intResult2
= getterChain(wsObjectNull, WSObject::getFoo, Foo::getBar, Bar::getBar, Baz::getInt);
System.out.println(intResult.result);
System.out.println(intResult.lastIndex);
System.out.println();
System.out.println(intResult2.result);
System.out.println(intResult2.lastIndex);
// TODO code application logic here
}
public static <R, V1, V2, V3, V4> GetterResult<R>
getterChain(V1 value, Getter<V1, V2> g1, Getter<V2, V3> g2, Getter<V3, V4> g3, Getter<V4, R> g4)
{
GetterResult result = new GetterResult<>();
Object tmp = value;
if (tmp == null)
return result;
tmp = g1.get((V1)tmp);
result.lastIndex++;
if (tmp == null)
return result;
tmp = g2.get((V2)tmp);
result.lastIndex++;
if (tmp == null)
return result;
tmp = g3.get((V3)tmp);
result.lastIndex++;
if (tmp == null)
return result;
tmp = g4.get((V4)tmp);
result.lastIndex++;
result.result = (R)tmp;
return result;
}
}
wsObject.getFoo().getBar().getBaz().getInt();
void myFunction()
{
try
{
if (wsObject.getFoo() == null)
{
throw new FooNotExistException();
}
return wsObject.getFoo().getBar().getBaz().getInt();
}
catch (Exception ex)
{
log.error(ex.Message, ex); // Write log to track whatever exception happening
throw new OperationFailedException("The requested operation failed")
}
}
void Main()
{
try
{
myFunction();
}
catch(FooNotExistException)
{
// Show error: "Your foo does not exist, please check"
}
catch(OperationFailedException)
{
// Show error: "Operation failed, please contact our support"
}
}
static <T> T get(Supplier<T> supplier, T defaultValue) {
try {
return supplier.get();
} catch (NullPointerException e) {
return defaultValue;
}
}
return get(() -> wsObject.getFoo().getBar().getBaz().getInt(), -1);
package com.todelete;
public class Test {
public static void main(String[] args) {
Address address = new Address();
address.setSomeCrap(null);
Person person = new Person();
person.setAddress(address);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
try {
System.out.println(person.getAddress().getSomeCrap().getCrap());
} catch (NullPointerException npe) {
}
}
long endTime = System.currentTimeMillis();
System.out.println((endTime - startTime) / 1000F);
long startTime1 = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
if (person != null) {
Address address1 = person.getAddress();
if (address1 != null) {
SomeCrap someCrap2 = address1.getSomeCrap();
if (someCrap2 != null) {
System.out.println(someCrap2.getCrap());
}
}
}
}
long endTime1 = System.currentTimeMillis();
System.out.println((endTime1 - startTime1) / 1000F);
}
}
public class Person {
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
package com.todelete;
public class Address {
private SomeCrap someCrap;
public SomeCrap getSomeCrap() {
return someCrap;
}
public void setSomeCrap(SomeCrap someCrap) {
this.someCrap = someCrap;
}
}
package com.todelete;
public class SomeCrap {
private String crap;
public String getCrap() {
return crap;
}
public void setCrap(String crap) {
this.crap = crap;
}
}
Snag<Car, String> ENGINE_NAME = Snag.createForAndReturn(Car.class, String.class).toGet("engine.name").andReturnNullIfMissing();
final String name = ENGINE_NAME.get(firstCar);
private int getFooBarBazInt() {
if (wsObject.getFoo() == null) return -1;
if (wsObject.getFoo().getBar() == null) return -1;
if (wsObject.getFoo().getBar().getBaz() == null) return -1;
return wsObject.getFoo().getBar().getBaz().getInt();
}
public static class MyFoo {
private int barBazInt;
public MyFoo(Foo foo) {
this.barBazInt = parseBarBazInt();
}
public int getBarBazInt() {
return barBazInt;
}
private int parseFooBarBazInt(Foo foo) {
if (foo() == null) return -1;
if (foo().getBar() == null) return -1;
if (foo().getBar().getBaz() == null) return -1;
return foo().getBar().getBaz().getInt();
}
}
return wsObject.getFooBarBazInt();
class WsObject
{
FooObject foo;
..
Integer getFooBarBazInt()
{
if(foo != null) return foo.getBarBazInt();
else return null;
}
}
class FooObject
{
BarObject bar;
..
Integer getBarBazInt()
{
if(bar != null) return bar.getBazInt();
else return null;
}
}
class BarObject
{
BazObject baz;
..
Integer getBazInt()
{
if(baz != null) return baz.getInt();
else return null;
}
}
class BazObject
{
Integer myInt;
..
Integer getInt()
{
return myInt;
}
}