空字符串的Optional实现

时间:2018-08-09 15:50:48

标签: java string optional java-9

关于Optional的最好的事情之一是,它可以将所有null值的样板检查保存在一条长链中:

Optional.ofNullable(myService.getSomething())
    .map(secondService::fetch)
    .map(thirdService::fetchAgain)
    // And so forth...

如果map返回null,则Optional随时会跳到“空”轨道上。

如果可以对字符串执行类似的操作,而不必每次都检查String::isEmpty,那就太好了

Optional.ofNullable(entity.getName())
    .filter(String::isEmpty)
    .map(Utils::performSomeOperation)
    .filter(String::isEmpty)
    .or(service::getMostCommonName)
    .filter(String::isEmpty)
    .orElse("Bob");

类似这样的东西:

OptionalString.ofEmptyable(entity.getName())
    .map(Utils::performSomeOperation)
    .or(service::getMostCommonName)
    .orElse("Bob");

“可选”中的关键逻辑发生在ofNullable中,它调用其对value == null的检查。从理论上讲,您可以在其中应用任何形式的逻辑:

MagicalOptionalString(StringUtils::isNotBlank).ofEmptyable(entity.getName())
    .map(Utils::performSomeOperation)
    .or(service::getMostCommonName)
    .orElse("Bob");

但是,Optional是最终的,阻止了任何直接的方式来扩展这种行为。那么,已经有一个强大的现有实施方案了吗?

2 个答案:

答案 0 :(得分:3)

尝试一些方法来解决您的目标,然后意识到我会从VGR那里接受想法,因为与使用现有方法相比,实现这种用例是很多额外的工作。

但是,在花了一些时间查看实现之后,我可以添加的一些细节-

作为实用程序,您可以实现一个静态实现,该实现针对字符串输入的nullisEmpty条件进行验证,并相应地返回Optional。该代码可能类似于-

private static Optional<String> ofEmptyable(String string) {
    return isNullOrEmpty(string) ? Optional.empty() : Optional.of(string);
}

private static boolean isNullOrEmpty(String target) {
    return target == null || target.isEmpty();
}

这可以代替ofNullable的使用,后者专门检查null(Optional的主要目的)。


由于问题陈述中的期望实际上是按照方法(map / or / orElse)调用中的实际情况处理,与可选方法类似,{{1 }}可以将自定义OptionalInt实现为-

OptionalString

可以通过以下方式针对您的用例进一步实现-

public final class OptionalString {

    private static final OptionalString EMPTY = new OptionalString();

    private final boolean isPresent;
    private final String value;

    private OptionalString() {
        this.isPresent = false;
        this.value = "";
    }

    private static OptionalString empty() {
        return EMPTY;
    }

    private boolean isPresent() {
        return isPresent;
    }

    private OptionalString(String value) {
        this.isPresent = true;
        this.value = value;
    }

    public static OptionalString of(String value) {
        return value == null || value.isEmpty() ? OptionalString.empty() : new OptionalString(value);
    }

    public OptionalString map(Function<? super String, ? extends String> mapper) {
        return !isPresent() ? OptionalString.empty() : OptionalString.of(mapper.apply(this.value));
    }

    public OptionalString or(Supplier<String> supplier) {
        return isPresent() ? this : OptionalString.of(supplier.get());
    }

    String orElse(String other) {
        return isPresent ? value : other;
    }

    public String getAsString() {
        return Optional.of(value).orElseThrow(() -> new NoSuchElementException("No value present"));
    }
}

其中

String customImpl = OptionalString.of(entity.getName())
            .map(OptionalStringTest::trimWhiteSpaces) // OptionalStringTest is my test class name where 'trimWhiteSpaces' operation on String resides
            .or(service::getMostCommonName)
            .orElse("learning");
System.out.println(String.format("custom implementation - %s", customImpl));

注意 -老实说,我找不到在JDK中未预先准备private static String trimWhiteSpaces(String x) { return x.trim(); } 类的原因(我要说明的原因)是因为我怀疑背后肯定有一个想法),我相信这只是我的触角半径小得多,我希望有人可以在这里添加更多细节。恕我直言,似乎使用OptionalString几乎所有您想要的东西都在那里,这使我们回到了循环的开始。

答案 1 :(得分:0)

对于在Kotlin工作的任何人,这真的很容易做到:

class NonEmptyString私有构造函数(val电子邮件:String){     随播对象工厂{         运算符fun invoke(value:String?):T? = value?.let {如果(it.isNotEmpty())NonEmptyString(value)else null}     } }

“静态” invoke函数根据其是否有效来有条件地创建一个新对象。并且允许您像构造函数(NonEmptyString(value))一样调用它。私有构造函数会强制您使用invoke方法。

因为如果无效,它将返回null,并且Kotlin内置了null安全性,因此链接起来真的很容易。添加mapflatMap函数非常简单。

有关我编写的更全面,更易理解的示例,请参见this Code Review question