如何使用Optional与未使用Optional创建的级联对象

时间:2017-11-13 11:01:13

标签: lambda java-8 optional flatmap

上下文:我链接了由wsdl2java生成的对象,因此它们都不包含java.util.Optional。有一个已经创建的方法调用soap Web服务,接收xml并在级联对象中解组它。

欲望:我想使用Optional来避免空测试。

我已经发布了我的dificults来完成它(how to use optional and filter along to retrieve an object from chained/cascaded objects generated by wsdl2java)但是,经过3天的搜索和阅读,我真的卡住了,我意识到我的知识中有一些差距因此我决定退后一步在继续前进之前尝试一个更简单的解决方案。

我想知道我是否真的尝试了一些可行的事情:对于没有使用Optional模式创建的级联对象使用Optional。我在互联网上找到的大多数例子都使用了用#34; private可选myObj"编码的级联对象。或者它们仅限于将Optional.of用于对象,而不是像wsdl2java生成的对象树。

到目前为止,我试图弄清楚是否有可能并且我也陷入困境:我跟着http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html并尝试应用同样的想法想象对象最初用Java 7编码(没有私有完全可选的myObj。

首先,正如Oracle文章所示(从父对子依赖):

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

}

成功的测试

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

}

现在,想象一下用Java 7模式创建的同样的计算机和Soundcar(USB类与上面相同)

声卡

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

}

计算机

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

}

即使没有编译的测试

@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")));
}

错误是:

可选类型中的flatMap(Function&gt;)方法不适用于参数      (电脑:: getSoundcard)      - 计算机类型中的getSoundcard()类型是声卡,这与描述符的返回类型不兼容:可选

***编辑

在现实世界中我有

NamenOndernemingType 1 X 1 NaamOndernemingLijstCommercieelType 1 X List<> NaamOndernemingType

我知道所有这三个都会产生相同的结果,除了一个细节:null安全

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

***未来的读者可能会觉得值得一读 how to use optional and filter along to retrieve an object from chained/cascaded objects generated by wsdl2java

我的最终解决方案成为:

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 个答案:

答案 0 :(得分:3)

为什么不使用.map()

因为根据Optional#flatMap()的java doc:

  

如果存在值,请将提供的Optional - 轴承映射函数应用于该值,返回该结果,否则返回空Optional。此方法<{>>类似至#map(Function),但提供的映射器的结果已为Optional ,如果已调用{{1}不会用额外的flatMap包装它。

flatMap中使用的映射器必须返回Optional。我认为Optional会更好地满足您的需求。因为它将map的结果包装在Optional。