如何在 Java 中实现函数式 If-Then-Else

问题描述 投票:0回答:1

鉴于以下

Function
实现...

从主数据库获取用户凭证

private Function<Long, Optional<UserCredentials>>
    getUserCredentialsFromMaster() {
        return userId -> Optional.ofNullable(userId)
            .flatMap(masterUserRepository::findById)
            .map(User::getCredentials);
}

从辅助数据库获取用户凭证

private Function<Long, Optional<UserCredentials>>
    getUserCredentialsFromSecondary() {
        return userId -> Optional.ofNullable(userId)
            .flatMap(secondaryUserRepository::findById)
            .map(User::getCredentials);
}

我需要执行

getUserCredentialsFromMaster
getUserCredentialsFromSecondary
,具体取决于
userId
来自哪里。

以下是我的尝试:

考虑域类

UserProfile

public class UserProfile {
    Long id;
    Long internalUserId; // if internalUserId is null then externalUserId is not
    Long externalUserId; // and vice-versa
}

尝试获得

UserCredentials
:

final UserProfile userProfile = userProfileRepository.findById(userProvileId);

final UserCredentials userCredentials =
    Optional.ofNullable(userProfile.internalUserId)
        .flatMap(getUserCredentialsFromMaster())
        .orElse(
            Optional.ofNullable(userProfile.externalUserId)
                .flatMap(getUserCredentialsFromSecondary())
                .orElseThrow(UserCredentialsNotFound::new));

internalUserId
不是
null
但上面的语句总是抛出
UserCredentialsNotFound
。我尝试将
getUserCredentialsFromMaster
getUserCredentialsFromSecondary
重写为从 if-then-else 块调用的普通 Java 方法,并且它按预期工作。

我错过了什么吗?

java option-type java-11 flatmap functional-java
1个回答
2
投票

TL;博士

您遇到了异常,因为

Optional.orElse()
的参数被急切地求值。 IE。即使调用它的
Optional
不为空,它也会被评估。

但正如你所说,要么 "

internalUserId
null
,然后
externalUserId
不是
null
,反之亦然"
orElseThrow()
会产生异常。

避免使用Optional来代替Null检查

首先,请注意

Optional
并不是为执行空检查而设计的。
Optional
的设计目标是充当返回类型,其方法
ofNullable()
应该包装一个可为空的返回值,而不是替代null-check

您可能有兴趣阅读:

隐式空检查并没有什么问题,当代码中有很多空检查时,这有点问题,但它是应用程序中特定行为的实现方式的直接后果,然后问题与该语言提供的工具。

解决此问题的更简洁方法是读取函数并定义一个基于提供的

Supplier
id
生成
UserRepository
的方法:

private Supplier<Optional<UserCredentials>> getUserCredentials(Long id, UserRepository repository) {
    
    return id == null ?
        Optional::empty :
        () -> repository.findById(id).map(User::getCredentials);
}

可以通过以下方式使用:

UserCredentials userCredentials = Stream.of(
        getUserCredentials(userProfile.internalUserId, masterUserRepository),
        getUserCredentials(userProfile.internalUserId, secondaryUserRepository)
    )
    .map(Supplier::get)
    .flatMap(Optional::stream)
    .findFirst()
    .orElseThrow(UserCredentialsNotFound::new);

如果您还想继续使用Optional

作为补救措施,您可以使用 Java 9

Optional.or()
,它期望
Supplier
Optional
,仅在需要时才会对其进行评估。

UserCredentials userCredentials = Optional.ofNullable(userProfile.internalUserId)
    .flatMap(getUserCredentialsFromMaster())
    .or(() ->
        Optional.ofNullable(userProfile.externalUserId)
            .flatMap(getUserCredentialsFromSecondary())
    )
    .orElseThrow(UserCredentialsNotFound::new);

或者,您可以使用

Optional.orElseGet()
,它采用
Supplier
的结果值:

UserCredentials userCredentials = Optional.ofNullable(userProfile.internalUserId)
    .flatMap(getUserCredentialsFromMaster())
    .orElseGet(() ->
        Optional.ofNullable(userProfile.externalUserId)
            .flatMap(getUserCredentialsFromSecondary())
            .orElseThrow(UserCredentialsNotFound::new)
    );
© www.soinside.com 2019 - 2024. All rights reserved.