空检查链与捕获NullPointerException


118

Web服务返回巨大的XML,我需要访问它的深层嵌套字段。例如:

return wsObject.getFoo().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;
}

或将其视为反模式?


29
我不太在意null检查,因为wsObject.getFoo().getBar().getBaz().getInt()已经是代码的味道了。阅读“ Demeter法则”是什么,并喜欢相应地重构代码。然后null检查的问题也将消失。并考虑使用Optional
汤姆

9
如何使用XPath并将其留给他们评估?
乔普·艾根

15
该代码可能是由生成的wsdl2java,它不符合Demeter定律。
阿德里安·考克斯

Answers:


143

捕获NullPointerException是一件非常有问题的事情,因为它们几乎可以在任何地方发生。从错误中获取一个错误,意外地捕获它,然后像一切正常一样继续进行非常容易,从而隐藏了一个真正的问题。处理起来非常棘手,因此最好完全避免。(例如,考虑对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 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);
}

为什么可选?

使用Optionals代替null可能不存在的值使该事实对于读者非常明显和清楚,并且类型系统将确保您不会意外忘记它。

您还可以访问更方便地使用这些值的方法,例如maporElse


缺席有效还是错误?

但是还要考虑中间方法返回null是否是有效结果,或者这是错误的征兆。如果总是错误,那么抛出异常可能比返回特殊值更好,或者对于中间方法本身抛出异常,则抛出异常更好。


也许更多的可选?

另一方面,如果中间方法中缺少的值仍然有效,也许您也可以将Optional它们切换为s?

然后,您可以像这样使用它们:

public Optional<Integer> mo(Ws wsObject) {
    return wsObject.getFoo()
        .flatMap(f -> f.getBar())
        .flatMap(b -> b.getBaz())
        .flatMap(b -> b.getInt());        
}

为什么不选?

我能想到的不使用的唯一原因Optional是,这是否在代码的性能关键部分,以及垃圾回收的开销是否是一个问题。这是因为Optional每次执行代码时都会分配一些对象,而VM 可能无法对其进行优化。在这种情况下,原始的if-tests可能会更好。


很好的答案。应该提到的是,如果还有其他可能的故障情况,并且需要区分它们,则可以使用Try代替Optional。尽管TryJava API中没有,但有许多库提供了一个,例如javaslang.iogithub.com / bradleyscollins
try4j

8
FClass::getBar等会更短。
蜘蛛鲍里斯(Boris)

1
@BoristheSpider:也许有点。但是我通常更喜欢lambdas而不是方法refs,因为通常类名要长得多,而且我发现lambdas有点容易阅读。
2016年

6
@Lii足够公平,但是请注意,方法引用可能会快一点,因为lambda可能需要更复杂的编译时构造。Lambda将需要生成static方法,这将产生很小的损失。
蜘蛛鲍里斯(Boris)

1
@Lii我实际上发现方法引用更简洁,更具描述性,即使它们稍长一些。
shmosel

14

我建议考虑Objects.requireNonNull(T obj, String message)。您可以使用每个异常的详细消息来构建链,例如

requireNonNull(requireNonNull(requireNonNull(
    wsObject, "wsObject is null")
        .getFoo(), "getFoo() is null")
            .getBar(), "getBar() is null");

我建议您不要使用特殊的返回值,例如-1。那不是Java风格。Java设计了异常机制来避免这种源自C语言的老式方式。

投掷NullPointerException也不是最好的选择。您可以提供自己的异常(将其选中以保证将由用户处理,或者不选中以进行更轻松的方式对其进行处理)或使用您正在使用的XML解析器中的特定异常。


1
Objects.requireNonNull最终抛出NullPointerException。因此,这与情况没有什么不同return wsObject.getFoo().getBar().getBaz().getInt()
Arka Ghosh

1
@ArkaGhosh,它也避免了ifOP显示的大量错误
Andrew Tobilko

4
这是唯一理智的解决方案。所有其他建议使用异常进行代码控制的流控制。附带说明一下:我认为由OP完成的方法链接也很奇怪。如果他将使用三个局部变量和相应的if,那么情况将更加清楚。此外,我认为问题不仅仅在于解决NPE:更深层的问题:OP应该问自己为什么吸气剂可以返回null。null是什么意思?也许一些空对象会更好?还是在有意义的异常中崩溃?基本上,一切都比流控制的例外要好。
Marius K.

1
使用异常来表示缺少有效返回值的无条件建议不是很好。当方法失败时,调用者很难从中恢复并在程序的其他部分的try-catch-statement中更好地处理该方法,则异常非常有用。为了简单地表示不存在返回值,最好使用Optional该类,或者返回一个可为空的值Integer
Lii

6

假设确实如此,假设类结构确实不受我们的控制,我认为按照问题中的建议抓住NPE确实是一个合理的解决方案,除非性能是主要问题。一个小的改进可能是包装throw / catch逻辑以避免混乱:

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);

return get(() -> wsObject.getFoo().getBar().getBaz().getInt(), "");不会在编译时出现错误,这可能是有问题的。
菲利普·乔斯菲

5

正如汤姆在评论中指出的那样,

以下陈述违反了Demeter法则

wsObject.getFoo().getBar().getBaz().getInt()

您想要的是什么int,您可以从中获取Foo得墨meter耳定律说,永远不要和陌生人说话。对于你的情况,你可以隐藏在引擎盖下的实际执行FooBar

现在,您可以在中创建要从中Foo获取方法。最终,将具有并且我们可以直接访问而无需访问。因此,空检查可能会划分为不同的类,并且仅必需的属性将在这些类之间共享。intBazFooBarBarIntBazFoo


4
如果它不遵守Demeter定律是有争议的,因为WsObject可能只是一个数据结构。看到这里:stackoverflow.com/a/26021695/1528880
DerM

2
@DerM是的,这是可能的,但是由于OP已经具有解析其XML文件的功能,因此他还可以考虑为所需的标签创建合适的模型类,以便解析库可以映射它们。然后,这些模型类包含null检查其自身子标签的逻辑。
汤姆(Tom)

4

我的答案与@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如果该对象有可能为null,则也可以添加null检查。


4

您说某些方法“可能会返回null”,但没有说明它们在什么情况下会返回null。您说自己抓住了,NullPointerException但是没有说为什么要抓住它。信息的缺乏表明您对什么是例外以及为什么它们优于替代方法没有清楚的了解。

考虑一个旨在执行动作的类方法,但是由于超出其控制范围的情况,该方法不能保证它将执行该动作(实际上,Java中所有方法都是这种情况)。我们调用该方法并返回。调用该方法的代码需要知道它是否成功。怎么知道 如何构造它以应付成功或失败的两种可能性?

使用异常,我们可以编写将成功作为后置条件的方法。如果该方法返回,则说明成功。如果抛出异常,则说明失败。为了清晰起见,这是一个巨大的胜利。我们可以编写清楚地处理正常成功案例的代码,并将所有错误处理代码移到catch子句中。它经常表明,方法失败的原因或细节对于调用者而言并不重要,因此同一catch子句可用于处理多种类型的失败。它经常发生的方法并不需要捕捉异常可言,但也只是让他们传播到它的调用者。由于程序错误而导致的异常在后一类中。发生错误时,很少有方法可以做出适当的反应。

因此,那些返回的方法null

  • null值是否表示代码中存在错误?如果是这样,那么您根本不应该捕获异常。而且您的代码不应试图自己进行第二次猜测。只要在可以使用的假设上写清楚清楚的内容即可。方法调用链是否简洁明了?然后使用它们。
  • null值是否表示对程序的无效输入?如果是这样,NullPointerException则抛出a 是不合适的异常,因为按惯例,它保留用于指示错误。您可能想要抛出一个自定义的异常,该异常源自于IllegalArgumentException(如果您想要一个未经检查的异常)或IOException(如果您想要一个检查的异常)。输入无效时,您的程序是否需要提供详细的语法错误消息?如果是这样,null您唯一可以做的就是检查每个方法的返回值,然后抛出适当的诊断异常。如果您的程序不需要提供详细的诊断信息,则将方法调用链接在一起,捕获任何方法NullPointerException,然后抛出自定义异常是最清晰,最简洁的方法。

答案之一声称,链接方法调用违反了Demeter定律,因此很糟糕。该说法是错误的。

  • 当涉及到程序设计时,实际上没有关于优缺点的绝对规则。只有启发式:规则在大多数时间(甚至几乎所有时间)都是正确的。编程技巧的一部分是知道何时可以打破这些规则。因此,一个简短的断言“这违反了规则X ”根本不是一个答案。这是应该违反规则的情况之一吗?
  • Demeter定律实际上是有关API或类接口设计的规则。设计类时,具有抽象层次结构很有用。您有使用语言原语直接执行操作并以比语言原语更高级别的抽象表示对象的低级类。您有中等级别的类,它们委派给低级别的类,并在比低级别的类更高的级别上实现操作和表示。您有高级类,它们委派给中级类,并实现更高级的操作和抽象。(我在这里只讨论了三个抽象级别,但是可能更多)。这使您的代码可以在每个级别上以适当的抽象表示自己,从而隐藏了复杂性。得墨meter耳定律的基本原理就是说,如果您有一连串的方法调用,则表明您有一个通过中级类直接访问低级细节的高级类,因此,您的中级类未提供中级抽象操作高层需要的 但是似乎这里不是这种情况:您没有在方法调用链中设计类,它们是一些自动生成的XML序列化代码的结果(对吗?),并且调用链没有下降通过抽象层次结构,因为反序列化的XML都处于抽象层次结构的同一级别(对吗?)?

3

为了提高可读性,您可能需要使用多个变量,例如

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();

我认为这很难理解。它用大量的空值检查逻辑乱丢了该方法,这与该方法的实际逻辑完全无关。
Developer102938

2

别抓住NullPointerException。您不知道它从哪里来(我知道您的情况不太可能,但是可能还有其他原因),而且速度很慢。您要访问指定的字段,为此,其他所有字段都不能为null。这是检查每个字段的完美有效理由。我可能会检查它是否一个,然后创建一种可读性的方法。正如其他人指出的那样,已经返回-1是很老套的,但是我不知道您是否有理由(例如与另一个系统对话)。

public int callService() {
    ...
    if(isValid(wsObject)){
        return wsObject.getFoo().getBar().getBaz().getInt();
    }
    return -1;
}


public boolean isValid(WsObject wsObject) {
    if(wsObject.getFoo() != null &&
        wsObject.getFoo().getBar() != null &&
        wsObject.getFoo().getBar().getBaz() != null) {
        return true;
    }
    return false;
}

编辑:如果它不符合Demeter法则,这是有争议的,因为WsObject可能只是一个数据结构(请检查https://stackoverflow.com/a/26021695/1528880)。


2

如果您不想重构代码,并且可以使用Java 8,则可以使用Method引用。

首先进行简单演示(请使用静态内部类)

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;
            }
}

输出量

241
4


2

该接口Getter只是一个功能性接口,您可以使用任何等效接口。
GetterResult类中,为清楚起见,删除了访问器,保留了getter链(如果有)或最后调用的getter的索引的结果。

该方法getterChain是一个简单的样板代码,可以自动生成(或在需要时手动生成)。
我对代码进行了结构设计,以便重复块显而易见。


这不是一个完美的解决方案,因为您仍然需要为getterChain每个吸气剂定义一个重载。

我将重构代码,但是如果不能重构,则您通常会使用长的getter链来发现自己,那么您通常可以考虑使用从2到10个getter的重载构建类。


2

正如其他人所说的那样,遵守德米特定律绝对是解决方案的一部分。另一部分是,尽可能地更改那些链接的方法,以使它们无法返回null。您可以null通过返回一个空的String,一个空的Collection或其他虚拟对象来避免返回,该对象意味着或执行调用者将要做的任何事情null


2

我想添加一个针对错误含义的答案。空异常本身不提供任何含义的完全错误。因此,我建议避免直接与他们打交道。

在成千上万种情况下,您的代码可能会出错:无法连接到数据库,IO异常,网络错误...如果您逐一处理它们(例如此处的null检查),那将是一件麻烦事。

在代码中:

wsObject.getFoo().getBar().getBaz().getInt();

即使您知道哪个字段为空,也不会知道哪里出了问题。也许Bar为空,但是可以预期吗?还是数据错误?考虑一下阅读您的代码的人

就像在xenteros的答案中一样,我建议使用自定义未检查的异常。例如,在这种情况下:Foo可以为空(有效数据),但Bar和Baz绝不能为空(无效数据)

该代码可以重写:

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"
    }
}

未经检查的异常表示程序员正在滥用API。外部问题,例如“无法连接到数据库,IO异常,网络错误”,应通过检查的异常来指示。
凯文·克鲁姆维德

这实际上取决于呼叫者的需求。已检查的异常帮助,因为它会迫使您处理错误。但是,在其他情况下,则没有必要,并且可能会污染代码。例如,您在数据层中有一个IOException,是否将其扔到Presentation层?这意味着您必须捕获异常并在每个呼叫者处重新抛出。我更喜欢用自定义BusinessException包裹IOException并附带一条相关消息,然后让它在堆栈跟踪中弹出,直到全局过滤器捕获到它并向用户显示消息为止。
2016年

调用者不必捕获并重新抛出已检查的异常,只需声明将它们抛出即可。
凯文·克鲁姆维德

@KevinKrumwiede:是的,我们只需要声明要抛出的异常即可。我们仍然需要声明。编辑:从第二个角度看,关于检查和未检查的异常用法有很多争论(例如:programmers.stackexchange.com/questions/121328/…)。
晃龙

2

NullPointerException 是运行时异常,因此通常不建议抓住它,而要避免它。

无论何时要调用该方法,您都必须捕获该异常(否则它将在堆栈中向上传播)。不过,如果在您的情况下,您可以继续使用值-1来处理该结果,并且确定它不会传播,因为您没有使用任何可能为空的“件”,那么我认为正确抓住它

编辑:

我同意@xenteros 的后续回答,最好启动您自己的异常,而不是返回-1 InvalidXMLException(例如可以调用它)。


3
“无论是否捕获它,它都可以传播到代码的其他部分”,这是什么意思?
绿巨人

如果此句子中为null wsObject.getFoo(),然后在代码的稍后部分再次运行该查询或使用wsObject.getFoo()。getBar()(例如),它将再次引发NullPointerException。
SCouto

这是一个不寻常的措辞,即“您必须在要调用该方法的任何地方捕获异常(否则它将在堆栈中向上传播)”。如果我理解正确的话。我同意这一点(这可能是一个问题),我只是觉得措辞令人困惑。
绿巨人

我会解决它,对不起,英语不是我的母语,所以有时可能会发生这种情况:)谢谢
SCouto

2

从昨天开始一直关注此帖子。

我一直在评论/投票说“赶上NPE不好”的评论。这就是我一直这样做的原因。

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;
    }
}

输出量

3.216

0.002

我在这里看到了明显的赢家。有检查是否比捕获异常便宜得多。我已经看到Java-8的工作方式。考虑到当前70%的应用程序仍在Java-7上运行,我添加了这个答案。

底线对于任何关键任务应用,处理NPE的成本很高。


在最坏的情况下,一百万个请求的额外三秒钟是可以测量的,但是即使在“关键任务应用程序”中,也很少会破坏交易。在某些系统中,向请求添加3.2微秒的时间很重要,如果您有这样的系统,则一定要仔细考虑异常。但是,按照最初的问题,调用Web服务并反序列化其输出可能花费的时间要长得多,并且担心异常处理的性能不在此列。
Jeroen Mostert

@JeroenMostert:每张支票3秒/百万。因此,支票的数量将增加成本
NewUser

真正。即使如此,我仍然认为它是“配置文件优先”的情况。在一个请求中,您将需要300多个支票,然后再花费整整毫秒的时间。设计方面的考虑比我早得多。
Jeroen Mostert

@JeroenMostert::)同意!我想把结果留给程序员,让他们打电话!
NewUser 2016年

1

如果效率是一个问题,则应考虑使用“捕获”选项。如果“抓”不能使用,因为它会传播(如“SCouto”提到),然后使用局部变量,以避免方法多次调用getFoo()getBar()getBaz()


1

值得考虑创建自己的异常。我们称之为MyOperationFailedException。您可以抛出它,而不是返回一个值。结果将是相同的-您将退出函数,但不会返回硬编码值-1(它是Java反模式)。在Java中,我们使用异常。

try {
    return wsObject.getFoo().getBar().getBaz().getInt();
} catch (NullPointerException ignored) {
    throw new MyOperationFailedException();
}

编辑:

根据评论中的讨论,让我在以前的想法中添加一些内容。在此代码中,有两种可能性。一种是您接受null,另一种是您接受此错误。

如果是错误并且发生,则可以在断点不足时使用其他结构调试代码以进行调试。

如果可以接受,则无需担心此null出现在何处。如果这样做,则绝对不应链接这些请求。


2
您是否认为抑制该异常不是一个坏主意?实时地,如果我们丢失了异常的痕迹,那么它真正的痛苦就在底层,以找出到底发生了什么!我总是建议不要使用链接。我看到的第二个问题是:这段时间无法授予该代码的权限,结果是null。
NewUser 2016年

不,您的Exception可能有一条消息,该消息肯定会指出引发该消息的位置。我同意链接不是最佳解决方案:)
xenteros

3
不,它只会说明行号。因此,链中的任何调用都可能导致异常。
NewUser

“如果发生错误并且可以发生,则可以调试代码”-不在生产中。我宁愿知道当我只有日志时会失败的原因,也不愿尝试查明导致失败的原因。有了该建议(和该代码),您真正知道的是四件事之一为空,但不是哪一个或为什么。
VLAZ

1

您使用的方法冗长,但可读性强。如果我是一名新开发人员来使用您的代码库,那么我可以很快地看到您在做什么。在我看来,大多数其他答案(包括捕获异常)似乎并没有使事情更具可读性,有的正在使事情的可读性降低。

假设您可能无法控制生成的源,并且假设您确实只需要在此处访问一些深度嵌套的字段,那么我建议使用一种方法包装每个深度嵌套的访问。

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();
}

如果您发现自己编写了很多这样的方法,或者如果您想使用这些公共静态方法,那么我将创建一个单独的对象模型,嵌套您想要的方式,仅包含您关心的字段,然后从Web转换服务对象模型到您的对象模型。

与远程Web服务进行通信时,通常具有“远程域”和“应用程序域”,并在两者之间进行切换。远程域通常受到Web协议的限制(例如,您不能在纯RESTful服务中来回发送辅助方法,并且深层嵌套的对象模型很常见,以避免多次API调用),因此不适合直接用于您的客户。

例如:

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();
    }

}

1
return wsObject.getFooBarBazInt();

通过应用得墨meter耳定律,

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;
    }
}

0

提供似乎与所有其他答案不同的答案。

我建议你检查NULLif秒。

原因:

我们不应为程序崩溃留任何机会。NullPointer由系统生成。无法预测系统生成的异常的行为。当您已经有一种自己处理程序的方法时,请勿将程序交给系统。并放置异常处理机制以提高安全性!!

为了使您的代码易于阅读,请尝试以下检查条件:

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()在一些变量。我没有这样做,因为我不知道该函数的返回类型。

任何建议将不胜感激..!


您是否认为getFoo()是一项非常耗时的操作?您应该将返回的值存储在变量中,但是这浪费了内存。您的方法非常适合C编程。
xenteros

但有时最好迟到1毫秒,然后程序崩溃@xenteros .. !!
Janki Gadhiya

getFoo()可能是从另一个大陆上的服务器获取值。它可以持续任何时间:分钟/小时...
xenteros

wsObject将包含从Webservice返回的值。该服务将已经被调用,wsObject并将获得长XML数据作为Web服务响应。因此,没有哪个服务器位于另一个大陆上,因为getFoo()这只是获取getter方法的元素,而不是Webservice调用.. !! @xenteros
Janki Gadhiya

1
从吸气剂的名称来看,我认为它们将返回Foo,Bar和Baz对象:P还应考虑从答案中删除提到的双重安全性。除了代码的污染之外,我认为它没有任何真正的价值。使用合理的局部变量和空值检查,我们所做的工作足以确保代码的正确性。如果可能发生异常,则应将其视为一个。
Marius K.

0

我写了一个名为的类Snag,它使您可以定义在对象树中导航的路径。这是一个用法示例:

Snag<Car, String> ENGINE_NAME = Snag.createForAndReturn(Car.class, String.class).toGet("engine.name").andReturnNullIfMissing();

这意味着该实例ENGINE_NAME将有效地调用Car?.getEngine()?.getName()传递给它的实例,并在返回null任何引用时返回null

final String name =  ENGINE_NAME.get(firstCar);

它没有在Maven上发布,但是如果有人发现它有用,就在这里(当然不做任何担保!)

这有点基本,但似乎可以完成工作。显然,它与支持安全导航或的Java和其他JVM语言的最新版本相比已经过时了Optional

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.