如何将 Optional 与不是使用 Optional 创建的级联对象一起使用

how use Optional with cascaded objects not created with Optional

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

愿望:我想使用 Optional 以避免空测试。

我已经发布了完成它的困难 () 但是,经过 3 天的搜索和阅读,我真的被卡住了,我意识到我的知识还存在一些差距,所以我决定迈出一步返回并尝试更简单的解决方案,然后再继续。

我想知道我是否真的在尝试可能做的事情:将 Optional 与级联对象一起使用,这些对象不是在考虑 Optional 模式的情况下创建的。我在互联网上找到的大多数示例要么使用用 "private Optional myObj" 编码的级联对象,要么仅限于对对象使用 Optional.of,而不是像 wsdl2java 生成的对象树。

到目前为止,这是我试图弄清楚是否可行的方法,但我也被卡住了:我遵循了 http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html 并且我尝试应用相同的想法,想象对象最初是用 [=68 编码的=] 7(根本没有 private Optional myObj)。

首先,完全如 Oracle 文章所示(从父到子依赖):

USB

public class USB {

    String version;
    public USB() {

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

可选声卡

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

}

可选计算机

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 class 与上面相同)

声卡

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>) 不适用于参数 (计算机::获取声卡) - Computer 类型的 getSoundcard() 类型是 Soundcard,这与描述符的 return 类型不兼容:Optional

*** 已编辑

在现实世界中我有

NamenOndernemingType 1 X 1 NaamOndernemingLijstCommercieelType 1 X List<> NaamOndernemingType

我知道所有这三个都会产生相同的结果,除了一个细节: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();

*** 未来的读者可能会发现值得一读

我的最终解决方案变成:

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

为什么不直接使用 .map()

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

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.

flatMap 中使用的映射器必须 return 和 Optional。我认为 map 更适合您的需求。因为它将 Computer#getSoundcard() 的结果包装在 Optional.