在Java中使用Optional和Lambda

时间:2019-02-12 13:33:36

标签: java java-8 functional-programming optional

我正在使用Java中的Optional和lambdas编写代码,并且想知道在以下情况下最好的方法是什么

public Optional<BObject> readIndexMaybe(String ref) {
    try {
        return Optional.ofNullable(index.read(ref)).map(BObjectBuilder::build);
    } catch (IOException e) {
        LOGGER.error(String.format("Could not read index of ref: %s, error: %s", ref, e));
    }
    return Optional.empty();
}

public Optional<BObject> readMaybe(String ref) {
    Optional<BObject> bObject = readIndexMaybe(ref);
    return bObject.flatMap(boMaybe -> {                <---- HERE
        try {
            LOGGER.debug(String.format("Object read: %s", ref));
            BObject obj = new BObjectBuilder(boMaybe).stream(loadDocumentStream(boMaybe)).build();
            return Optional.of(obj);
        } catch (IOException e) {
            LOGGER.error(String.format("Could not read file with ref: %s, error: %s", ref, e));
        }

        return Optional.empty();
    });
}

使用返回一个Optional<BObject>的lambda函数,然后在其上使用flatMap来接收Optional<BObject>作为返回类型,还是返回{{ 1}},然后使用null

map

第一种方法对我来说似乎更好一些,因为我可以在其他地方重用lambda函数,它不会返回public Optional<BObject> readIndexMaybe(String ref) { try { return Optional.ofNullable(index.read(ref)).map(BObjectBuilder::build); } catch (IOException e) { LOGGER.error(String.format("Could not read index of ref: %s, error: %s", ref, e)); } return Optional.empty(); } public Optional<BObject> readMaybe(String ref) { Optional<BObject> bObject = readIndexMaybe(ref); return bObject.map(boMaybe -> { <--- HERE try { LOGGER.debug(String.format("Object read: %s", ref)); return new BObjectBuilder(boMaybe).stream(loadDocumentStream(boMaybe)).build(); } catch (IOException e) { LOGGER.error(String.format("Could not read file with ref: %s, error: %s", ref, e)); } return null; }); } 而是返回null。但是,只要我只在一个地方使用它,就值得吗?

2 个答案:

答案 0 :(得分:1)

如果可以选择,我建议编写返回Optional而不是null的函数,如果无结果是正常的预期结果。因此,您将使用flatMap来调用此类函数。 map操作对于始终返回有效结果的函数很有用。它还会将空返回值转换为空的Optional,这在您需要适应无法更改的空返回代码时尤其有用。

通常的建议是提取一种方法而不是使用多行语句lambda。然后,在flatMap操作中使用简单的lambda或方法引用。

此外,对于try / catch块,我建议将try-clause中的代码最小化,仅将那些实际上可以引发您捕获的异常的代码,并在try-statement语句之外进行其他转换。在这种情况下,我假设index.read(ref)loadDocumentStream(boMaybe)是可以抛出IOException的语句。请注意,这意味着局部变量需要保存临时结果,并且它可以为空。我认为可以。空值处理非常本地化,它使您可以将返回的Optional的创建合并到单个表达式中。

最后,我建议不要将后缀“也许”用作可选选项。这很令人困惑,并且在示例中,用于boMaybe的lambda参数flatMap是错误的。仅当存在该值时才对该lambda进行评估,因此它没有“可能”存在。

应用所有这些建议将得到结果代码:

Optional<BObject> readIndex(String ref) {
    Index i = null;
    try {
        i = index.read(ref);
    } catch (IOException e) {
        LOGGER.error(/*...*/);
    }
    return Optional.ofNullable(i).map(BObjectBuilder::build);
}

Optional<BObject> load(BObject bo) {
    DocStream docStream = null;
    try {
        LOGGER.debug(/*...*/);
        docStream = loadDocumentStream(bo);
    } catch (IOException e) {
        LOGGER.error(/*...*/);
    }
    return Optional.ofNullable(docStream)
                   .map(ds -> new BObjectBuilder(bo).stream(ds).build());
}

Optional<BObject> read(String ref) {
    return readIndex(ref).flatMap(bo -> load(bo)); // or this::load
}

答案 1 :(得分:0)

您可能想提供一个稳定且安全的api,这就是为什么您使用Optional的原因,尽管在您的情况下它会使事情变得有些复杂,因为您需要捕获已检查的异常。我建议这种方法:

public Optional<BObject> readIndexMaybe(String ref) {
   try {
       return Optional.ofNullable(index.read(ref)).map(BObjectBuilder::build);
   } catch (IOException e) {
       LOGGER.error(String.format("Could not read index of ref: %s, error: %s", ref, e));
   }
   return Optional.empty();
}

public Optional<BObject> readMaybe(String ref) {
   Optional<BObject> bObject = readIndexMaybe(ref);
   if(!bObject.isPresent()){
      return bObject; // is same as Optional.empty()
   }
   BObject boMaybe = bObject.get();
   try {
      LOGGER.debug(String.format("Object read: %s", ref));
      boMaybe = new BObjectBuilder(boMaybe).stream(loadDocumentStream(boMaybe)).build();
      return Optional.of(boMaybe);
   } catch (IOException e) {
      LOGGER.error(String.format("Could not read file with ref: %s, error: %s", ref, e));
      return Optional.empty();
   }
}

这与您的地图方法非常相似,但我认为它更清楚,因为您在lambda中没有try-catch