Как заменить несколько операторов If на Stream API
Как можно стримами заменить конструкцию из нескольких if/else-if
?
if (!StringUtils.isEmpty(request.name())) {
return userRepository.findByName(request.name());
} else if (!StringUtils.isEmpty(request.lastname())) {
return userRepository.findByLastname(request.lastname());
} else if (!StringUtils.isEmpty(request.patronymic())) {
return userRepository.findByPatronymic(request.patronymic());
} else if (!StringUtils.isEmpty(request.email())) {
return userRepository.findByEmail(request.email());
}
Ответы (2 шт):
Можно попробовать так:
Optional.ofNullable(name)
.map(userRepository::findByName)
.or(() ->
Optional.ofNullable(lastName)
.map(userRepository::findByLastName)
)
.or(() ->
Optional.ofNullable(patronimic)
.map(userRepository::findByPatronimic)
).orElse(null);
Но по мне это не лучше.
Предыдущее решение от @talex скорее заменяет множественные if
при помощи Optional::or
(Java 9+).
Решение с использованием стримов может выглядеть так:
- определяем некую мапу с ключами-геттерами
Function<MyRequest, String>
и значениями-ссылками на методы репозиторияFunction<String, List<UserEntity>>
- берем стрим по элементам мапы, ищем первый подходящий элемент (геттер, возвращающий непустое значение), и для него вызываем соответствующий метод репозитория:
private UserRepository userRepository = new UserRepositoryImpl(); // реализация репозитория
Map<Function<MyRequest, String>, Function<String, List<UserEntity>>> map = new LinkedHashMap<>();
{
map.put(MyRequest::name, userRepository::findByName);
map.put(MyRequest::lastname, userRepository::findByLastname);
map.put(MyRequest::patronymic, userRepository::findByPatronymic);
map.put(MyRequest::email, userRepository::findByEmail);
}
public List<UserEntity> getUsers(MyRequest request) {
return map.entrySet().stream()
.filter(e -> !StringUtilities.isEmpty(e.getKey().apply(request)))
.findFirst() // Optional<Map.Entry<F1, F2>>
.map(e -> e.getValue().apply(e.getKey().apply(request)))
.orElseGet(Collections::emptyList);
}
Обновление:
Аналогично можно определить статическую мапу, тогда потребуется использовать BiFunction
для ссылки на метод репозитория UserRepository::findByXxx
, а при вызове apply
надо будет также передать экземпляр репозитория как показано ниже:
private static final Map<Function<MyRequest, String>, BiFunction<UserRepository, String, List<UserEntity>>> map = new LinkedHashMap<>();
static {
// ссылки на метод интерфейса UserRepository
map.put(MyRequest::name, UserRepository::findByName);
map.put(MyRequest::lastname, UserRepository::findByLastname);
map.put(MyRequest::patronymic, UserRepository::findByPatronymic);
map.put(MyRequest::email, UserRepository::findListByEmail);
}
public List<UserEntity> getUsers(MyRequest request) {
return map.entrySet().stream()
.filter(e -> notEmpty(e.getKey().apply(request)))
.findFirst() // Optional<Map.Entry<F1, BiF>>
.map(e -> e.getValue().apply(
userRepository, // передаём *экземпляр* репозитория
e.getKey().apply(request)
))
.orElseGet(Collections::emptyList);
}
Если окажется, что не все методы репозитория возвращают одинаковый тип, например, емайл считается уникальным и соответственно метод репозитория возвращает Optional
, потребуется дописать метод-конвертор, чтобы получить более общую коллекцию:
interface UserRepository {
// ...
Optional<UserEntity> findByEmail(String value);
default List<UserEntity> findList(Optional<UserEntity> opt) {
return opt.map(Collections::singletonList)
.orElseGet(Collections::emptyList);
}
default List<UserEntity> findListByEmail(String email) {
return this.findList(findByEmail(email));
}
}
Тогда при инициализации мапы соответственно придётся использовать корректную ссылку на метод userRepository::findListByEmail
.