how use Optional with cascaded objects not created with Optional

拥有回忆 提交于 2019-12-08 12:37:29

问题


Context: I have chained objects generated by wsdl2java so none of them contains java.util.Optional. There is an already created method that call the soap web services, receives the xml and unmarshalling it in cascaded objects.

Desire: I want to use Optional in order to avoid null tests.

I have already posted my dificults to accomplish it (how to use optional and filter along to retrieve an object from chained/cascaded objects generated by wsdl2java) but, after 3 days searching and reading I really got stuck and I realised that I have some gap in my knowlodge so I decided take a step back and try a more simple solution before moving forward.

I am wondering if I am really trying something that is possible to do: use Optional with a cascaded objects that weren't created with Optional pattern in mind. Most of the example I found in internet either use cascaded objects coded with "private Optional myObj" or they are limited to use Optional.of to an object, not to a tree of objects like generated by wsdl2java.

Here is what I tried so far to figure out if it is possible and I got stuck as well: I followed http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html and I tryed to apply same idea imagining the objects were coded originally with Java 7 (no private Optional myObj at all).

Firstly, exactly as showed by Oracle article (from parent to child dependency):

USB

public class USB {

    String version;
    public USB() {

    }
    public String getVersion() {
        return version;
    }
    public void setVersion(String version) {
        this.version = version;
    }
}

OptionalSoundcard

public class OptionalSoundcard {

    private Optional<USB> usb;

    public OptionalSoundcard() {
        // TODO Auto-generated constructor stub
    }

    public Optional<USB> getUsb() {
        return usb;
    }

    public void setUsb(Optional<USB> usb) {
        this.usb = usb;
    }

}

OptionalComputer

public class OptionalComputer {

    private Optional<OptionalSoundcard> soundcard;  

    public OptionalComputer() {
    }

    public Optional<OptionalSoundcard> getSoundcard() {
        return soundcard;
    }

    public void setSoundcard(Optional<OptionalSoundcard> soundcard) {
        this.soundcard = soundcard;
    }

}

And the well-successed test

@Test
public void runOptionalClassicOracleExample() throws Exception {

    USB usb = new USB();
    usb.setVersion("1");

    OptionalSoundcard soundcard = new OptionalSoundcard();
    soundcard.setUsb(Optional.ofNullable(usb));

    OptionalComputer computer = new OptionalComputer();
    computer.setSoundcard(Optional.ofNullable(soundcard));

    Optional<OptionalComputer> sc = Optional.of(computer);
    // Optional<Computer> sc = Optional.ofNullable(computer);
    String v1 = sc.flatMap(OptionalComputer::getSoundcard).flatMap(OptionalSoundcard::getUsb).map(USB::getVersion)
            .orElse("UNKNOWN");

    assertThat(v1, is(equalTo("1")));

}

Now, imagine same Computer and Soundcar created with Java 7 pattern in mind (USB class is the same as above)

Soundcard

public class Soundcard {

    private USB usb;

    public Soundcard() {
        // TODO Auto-generated constructor stub
    }

    public USB getUsb() {
        return usb;
    }

    public void setUsb(USB usb) {
        this.usb = usb;
    }

}

Computer

public class Computer {

    private Soundcard soundcard;  

    public Computer() {
        // TODO Auto-generated constructor stub
    }

    public Soundcard getSoundcard() {
        return soundcard;
    }

    public void setSoundcard(Soundcard soundcard) {
        this.soundcard = soundcard;
    }

}

And the test that even doesn't compile

@Test
public void runClassicOracleExample() throws Exception {

    USB usb = new USB();
    usb.setVersion("2");

    Soundcard soundcard = new Soundcard();
    soundcard.setUsb(usb);

    Computer computer = new Computer();
    computer.setSoundcard(soundcard);

    Optional<Computer> sc = Optional.ofNullable(computer);
    String v1 = sc.flatMap(Computer::getSoundcard).flatMap(Soundcard::getUsb).map(USB::getVersion)
            .orElse("UNKNOWN");

    assertThat(v1, is(equalTo("2")));
}

The error is:

The method flatMap(Function>) in the type Optional is not applicable for the arguments (Computer::getSoundcard) - The type of getSoundcard() from the type Computer is Soundcard, this is incompatible with the descriptor's return type: Optional

*** edited

in real world I have

NamenOndernemingType 1 X 1 NaamOndernemingLijstCommercieelType 1 X List<> NaamOndernemingType

I understand that all these three will produce the same results, except for one detail: null safety

// no Optional at all and no map() at all
NaamOndernemingLijstCommercieelType naamOndernemingLijstCommercieel = onderneming.getNamen()
        .getCommercieleNamen();
NaamOndernemingType naamOnderneming1 = naamOndernemingLijstCommercieel.getCommercieleNaam().stream()
        .filter(x -> x.getTaalcode().getValue() == "nl").findFirst().get();

// Optional.ofNullable wrapped only the list and flatMap the list
Optional<List<NaamOndernemingType>> optionalList = Optional
        .ofNullable(onderneming.getNamen().getCommercieleNamen().getCommercieleNaam());
NaamOndernemingType naamOnderneming2 = optionalList
        .flatMap(list -> list.stream().filter(s -> "nl".equals(s.getTaalcode().getValue())).findFirst()).get();

// Optional.ofNUllable on root element and map all other "levels" until get the
// list and strem()
Optional<NamenOndernemingType> optionalNamenOnderneming = Optional.ofNullable(onderneming.getNamen());
NaamOndernemingType naamOnderneminge = optionalNamenOnderneming.map(NamenOndernemingType::getCommercieleNamen)
        .map(NaamOndernemingLijstCommercieelType::getCommercieleNaam).get().stream().filter(Objects::nonNull)
        .filter(x -> x.getTaalcode().getValue() == "nl").findFirst().get();

*** Future readers may find worth to read how to use optional and filter along to retrieve an object from chained/cascaded objects generated by wsdl2java

My final solution become:

Optional.ofNullable(onderneming.getNamen()).map(NamenOndernemingType::getCommercieleNamen)
                .map(NaamOndernemingLijstCommercieelType::getCommercieleNaam).get().stream().filter(Objects::nonNull)
                .filter(x -> x.getTaalcode().getValue() == "nl").findFirst()
                .ifPresent(o -> myMethod("onderneming_commerciele_naam", o.getNaam().getValue()));

回答1:


Why not just use .map()?

Because according to java doc of Optional#flatMap():

If a value is present, apply the provided Optional-bearing mapping function to it, return that result, otherwise return an empty Optional. This method is similar to #map(Function), but the provided mapper is one whose result is already an Optional, and if invoked, flatMap does not wrap it with an additional Optional.

The mapper used in flatMap must return an Optional. I think map will suit your needs better. because it wraps the result from Computer#getSoundcard() in an Optional.



来源:https://stackoverflow.com/questions/47262770/how-use-optional-with-cascaded-objects-not-created-with-optional

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!