Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how use only higher-order functions to check if the return of web service is null using Optional and avoid noSuchElementException

Context: I have a web service which return a family with its menbers. A family will always have a father and a mother and no child or multiple childreen. The service is described below by its wsdl.

Purpose: I want to use effectively the Optional from Java 8 and avoid classical way to check null. By classical, I mean the way we were used to accomplish until Java 7.

If I assume that the webservice will always return a family this would be enough:

@Test
public void test1() {
    Family f = helloWorldClientImplBean.allFamily();

    f.getChildren().stream().filter(x -> x.getFirstName().equalsIgnoreCase("John")).findFirst()
            .ifPresent(y -> System.out.println(y.getLastName()));
}

I tested and I could see that, as long as I get a family answered by service, it would perfectly works not matter if I have children or not. I mean, in the service implementation below, if I commented the olderSon and youngSon code there will be no null exception at all.

The problem raises when the service return null.

After read several blogs and discussion about it I reach this code which properly check if the return of service was null.

@Test
public void testWorkingButSeemsOdd() {

    //Family f = helloWorldClientImplBean.allFamily();
    Family f = null; //to make simple the explanation

    Optional<Family> optFamily = Optional.ofNullable(f);

    if (optFamily.isPresent()) {

        optFamily.filter(Objects::nonNull).map(Family::getChildren).get().stream().filter(Objects::nonNull)
                .filter(x -> x.getFirstName().equalsIgnoreCase("John")).findFirst()
                .ifPresent(y -> System.out.println("Optional: " + y.getLastName()));

    }

What would be more clean for me would be one of these approaches (all of them are failling but I believe they can show what I have been trying to do):

// here I try to filter if f is not null before mapping

@Test
public void testFilterNonNull() {
    Family f = null;
    Optional.ofNullable(f).filter(Objects::nonNull).map(Family::getChildren).get().stream().filter(Objects::nonNull)
            .filter(x -> x.getFirstName().equalsIgnoreCase("John")).findFirst()
            .ifPresent(y -> System.out.println(y.getLastName()));

}

I know the next doesn't compile but I guess it is possible to reach something similar

@Test
@Ignore
public void testOptionalNullable() {
    Family f = helloWorldClientImplBean.allFamily();

    Optional.ofNullable(f).orElse(System.out.println("Family is null")).map(Family::getChildren).get().stream().filter(Objects::nonNull)
            .filter(x -> x.getFirstName().equalsIgnoreCase("John")).findFirst()
            .ifPresent(y -> System.out.println(y.getLastName()));

}

wsdl

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions targetNamespace="http://codenotfound.com/services/helloworld"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://codenotfound.com/services/helloworld"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    name="HelloWorld">

    <wsdl:types>
        <schema targetNamespace="http://codenotfound.com/services/helloworld"
            xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://codenotfound.com/services/helloworld"
            elementFormDefault="qualified" attributeFormDefault="unqualified"
            version="1.0">

            <element name="family">
                <complexType>
                    <sequence>
                        <element name="father" type="tns:persontype" minOccurs="1"
                            maxOccurs="1" />
                        <element name="mother" type="tns:persontype" minOccurs="1"
                            maxOccurs="1" />
                        <element name="children" type="tns:persontype" minOccurs="0"
                            maxOccurs="unbounded" />
                    </sequence>
                </complexType>
            </element>

            <complexType name="persontype">
                <sequence>
                    <element name="firstName" type="xsd:string" />
                    <element name="lastName" type="xsd:string" />
                </sequence>
            </complexType>

            <element name="EmptyParameter" type="tns:voidType" />

            <complexType name="voidType">
                <sequence />
            </complexType>
        </schema>
    </wsdl:types>

    <!-- Message -->

    <wsdl:message name="emptyRequest">
        <wsdl:part name="emptyParameter" element="tns:EmptyParameter" />
    </wsdl:message>

    <wsdl:message name="allFamiliesResponse">
        <wsdl:part name="allFamiliesResponse" element="tns:family" />
    </wsdl:message>

    <!-- PortType -->

    <wsdl:operation name="allFamilies">
            <wsdl:input message="tns:emptyRequest" />
            <wsdl:output message="tns:allFamiliesResponse"></wsdl:output>
        </wsdl:operation>
    </wsdl:portType>

    <!-- Binding -->

    <wsdl:binding name="HelloWorld_Binding" type="tns:HelloWorld_PortType">
        <soap:binding style="document"
            transport="http://schemas.xmlsoap.org/soap/http" />

        <wsdl:operation name="allFamilies">
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>

    <wsdl:service name="HelloWorld_Service">
        <wsdl:port name="HelloWorld_Port" binding="tns:HelloWorld_Binding">
            <soap:address location="http://localhost:9090/cnf/services/helloworld" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

relevant part of service implementation:

@Override
public Family allFamilies(VoidType emptyParameter) {
    ObjectFactory factory = new ObjectFactory();
    Family result = factory.createFamily();
    Persontype father = new Persontype();
    father.setFirstName("Jose");
    father.setLastName("Pereira");

    Persontype mother = new Persontype();
    mother.setFirstName("Maria");
    mother.setLastName("Pereira");

    result.setFather(father);
    result.setMother(mother);


    Persontype olderSon = new Persontype();
    olderSon.setFirstName("John");
    olderSon.setLastName("Pereira");

    Persontype youngerSon = new Persontype();
    youngerSon.setFirstName("Ana");
    youngerSon.setLastName("Pereira");
    result.getChildren().add(olderSon);
    result.getChildren().add(youngerSon);

    return result;
}

So, my straight question is: based on my scenario described above with wsdl and its implementation, is the really only way to check if the return from web service is null by using isPresent() in a very similar way we were used to do with classical null checks (if (f != null){...)?

like image 570
Jim C Avatar asked Dec 06 '25 09:12

Jim C


2 Answers

The main misconception is to assume that there was a need to do operations like .filter(Objects::nonNull) on an optional. It would defeat the entire purpose of optionals, if such filtering was necessary for empty optionals. Especially, as the result of filtering is again an empty optional when the predicate evaluates to false, bringing you back to square one.

In fact, .filter(Objects::nonNull) has the same effect as .filter(x -> true), for non-empty optionals it’s always true, for empty optionals it will never get evaluated anyway.

Further, you are switching back to an if statement despite you already know about ifPresent. So one straight-forward solution derived from your original code would be

Optional.ofNullable(helloWorldClientImplBean.allFamily())
        .ifPresent(f -> f.getChildren().stream()
            .filter(x -> x.getFirstName().equalsIgnoreCase("John"))
            .findFirst()
            .ifPresent(y -> System.out.println(y.getLastName()));

You can reduce the nested part by changing the operation to

Optional.ofNullable(helloWorldClientImplBean.allFamily())
        .flatMap(f -> f.getChildren().stream()
            .filter(x -> x.getFirstName().equalsIgnoreCase("John"))
            .findFirst())
        .ifPresent(y -> System.out.println(y.getLastName()));

This only addresses the issue you described in your question, i.e. that the service allFamily() might return null. You have also included a new null check into the stream operation that would handle cases where child instances are null.

If that really is required, the best solution would be a kick into the butt of whoever is responsible for the service implementation, but anyway, the second best solution is to simply do

Optional.ofNullable(helloWorldClientImplBean.allFamily())
        .flatMap(f -> f.getChildren().stream()
            .filter(x -> x!=null && x.getFirstName().equalsIgnoreCase("John"))
            .findFirst())
        .ifPresent(y -> System.out.println(y.getLastName()));

which is simpler than inserting an additional .filter(Objects::nonNull) into the stream.

like image 144
Holger Avatar answered Dec 09 '25 23:12

Holger


For handling the result with Optional, please refer to Holger's answer. Here I would like to take a different approach.

Make yourself one single question: why would you need Optional to handle this case at all? Is it to avoid using a null check with an if block on the returned Family value?

Consider this code:

Family f = helloWorldClientImplBean.allFamily();

if (f != null) {
    f.getChildren().stream()
        .filter(x -> x.getFirstName().equalsIgnoreCase("John"))
        .findFirst()
        .ifPresent(y -> System.out.println(y.getLastName()));
}

It is crystal-clear, easy to read and easy to maintain.

Now consider, i.e. a code like this:

Optional.ofNullable(helloWorldClientImplBean.allFamily())
    .map(Family::getChildren)
    .map(Collection::stream)
    .map(stream -> stream.filter(x -> "John".equalsIgnoreCase(x.getFirstName())))
    .flatMap(Stream::findFirst)
    .map(Persontype::getLastName)
    .ifPresent(System.out::println);

This code is functional style. All the operations are performed over the Optional via Optional.map operations, except for the one that returns the first element of the stream, which is accomplished via Optional.flatMap. The stream is handled step by step and not as a one-liner. Every operation performed over the Optional is null safe (by this I mean that I'm not only checking whether the returned initial Family instance is null, but also i.e. if f.getChildren() returns null as well).

Which version is shorter? Which one is more elegant? Which one clearer and easier to understand? Which one expresses the intention of the programmer in the best way?

I know my answer...

like image 23
fps Avatar answered Dec 09 '25 23:12

fps



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!