【问题标题】:how use Optional with cascaded objects not created with Optional如何将 Optional 与未使用 Optional 创建的级联对象一起使用
【发布时间】:2018-04-26 01:02:15
【问题描述】:

上下文:我已经链接了 wsdl2java 生成的对象,所以它们都不包含 java.util.Optional。有一个已经创建的方法可以调用肥皂网络服务,接收 xml 并将其解组到级联对象中。

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

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

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

到目前为止,这是我试图弄清楚是否可行,但我也陷入了困境:我关注 http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html 并尝试应用相同的想法,想象这些对象最初是用 Java 7 编码的(没有私有 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 类与上面相同)

声卡

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

错误是:

Optional 类型中的方法 flatMap(Function>) 不适用于参数 (计算机::getSoundcard) - Computer 类型中 getSoundcard() 的类型是 Soundcard,这与描述符的返回类型不兼容:可选

***已编辑

在现实世界中我有

NamenOndernemingType 1 X 1 NaamOndernemingLijstCommercieelType 1 X List<> NaamOndernemingType

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

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

【问题讨论】:

    标签: lambda java-8 optional flatmap


    【解决方案1】:

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

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

    如果存在值,则对其应用提供的Optional-bearing 映射函数,返回该结果,否则返回空的Optional。此方法与#map(Function)相似但提供的映射器的结果已经是Optional,如果被调用,flatMap 不会用一个额外的Optional

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

    【讨论】:

    • 谢谢。假设我有一个清单?例如:假设“public Soundcard getSoundcard”是“public List getSoundcard()”,我想过滤并只返回版本 = 1 的 SoundCard。我知道我必须使用 flatmap。我知道上面的例子很简单,但是当你在上下文中思考时我的问题变得更加严重:wsdl2java 生成的链式对象。
    • @JimC 这样的事情怎么样? optionalList.flatMap(list -&gt; list.stream().filter(s -&gt; s.getVersion() == 1).findFirst())
    • 我即将弥补我的知识差距。我在上面粘贴了三个 sn-ps,它们只会带来一个与语言“nl”匹配的元素。除非我对更基本的事情感到困惑,否则他们将执行完全相同的过滤器,但每个过滤器对 null 可能性的处理方式不同。您能否确认这是否正确:第一个根本没有零安全性。这意味着“级别”中的任何对象都会抛出空异常。第二个不会很有效,因为它缺少前两个“级别”,第三个是正确的方法,因为从根节点(第一级)开始检查
    • @JimC 它们看起来不错,但要小心最后两种方法 (.findFirst().get()),因为这样您可以在没有可用元素时获得 NoSuchElementException。省略 get()-call 并将其存储为 Optional。然后检查它是否存在(optional.isPresent()
    猜你喜欢
    • 1970-01-01
    • 2021-06-06
    • 2019-08-29
    • 1970-01-01
    • 1970-01-01
    • 2014-05-08
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多