Необязательный isPresent vs orElse (null)
Я обновлял зависимости Spring 5 в своем проекте и был обстрелян ошибками компиляции, где определение метода findOne()
было заменено на findById()
которое теперь возвращает Optional
(исправьте меня, если я ошибаюсь).
Во время рефакторинга я сталкивался с несколькими подходами, которые я могу принять, и поэтому мне хотелось бы, чтобы некоторые из них были предпочтительнее.
1-й подход:
ExpectedPackage ep = expectedPackageRepository.findById(1).orElse(null);
if(ep != null){
ep.setDateModified(new Date());
expectedPackageRepository.saveAndFlush(ep);
}
Второй подход:
Optional<ExpectedPackage> ep = expectedPackageRepository.findById(1);
if(ep.isPresent()){
ep.get().setDateModified(new Date());
expectedPackageRepository.saveAndFlush(ep.get());
}
Или есть третий и лучший подход, который я пропустил? Я рассмотрел несколько вопросов и пару статей, но я не нашел ясного ответа.
Ответы
Ответ 1
Вы также можете сделать:
expectedPackageRepository.findById(1).ifPresent(
ep -> {
ep.setDateModified(new Date());
expectedPackageRepository.saveAndFlush(ep);
}
);
В идеале вы также должны извлекать часть между скобками ({}
) отдельным методом. Тогда вы можете написать вот так:
expectedPackageRepository.findById(1).ifPresent(this::doSomethingWithEp);
Куда:
void doSomethingWithEp(ExpectedPackage ep) {
ep.setDateModified(new Date());
expectedPackageRepository.saveAndFlush(ep);
}
Вы можете прочитать документацию ifPresent
здесь: https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#ifPresent-java.util.function.Consumer-
Как он утверждает, он выполнит указанное действие, если значение присутствует и ничего не делает.
Ответ 2
Другой ответ - это, по сути, некоторый рефакторинг вашего второго подхода, который не имеет ничего общего с самим собой, это просто вопрос стиля. Конечно, цепочка и извлечение в отдельный метод сделают это намного более понятным и понятным, без сомнения (+1 от меня), тем более, что правильное использование ifPresent
.
Я просто добавлю здесь, что get
, ну, было замечено как-то ошибка дизайна (или может быть неправильным именем метода, вероятно, из-за мышления guava
). Использование get
даже если оно задокументировано, чтобы выбросить исключение, когда это значение отсутствует, как-то странно (если вы думаете, что здесь есть геттеры, вы не ожидаете, что getter
выбросит исключение). И вы не ожидаете, что get
нужно вызвать после isPresent
, по крайней мере, не в первых взаимодействиях с Optional
. Таким образом, get
был предложен как устаревший (и, надеюсь, удаленный), поэтому java-10 добавляет лучшее дополнение orElseThrow()
- это имеет смысл сразу после его чтения, потому что метать часть находится в имени метода, поэтому никаких сюрпризов.
Кроме того, кто-то должен рассказать вам об этом использовании new Date()
которая при использовании с Optional
из java-8 выглядит просто странно, теперь есть гораздо лучшие классы, связанные с временем и датой.
Я также не очень уверен, почему вы обновляете измененную дату вручную, когда есть весенние аннотации для таких, как PreUpdate/PrePersist
.
Ответ 3
Да, есть и другие подходы.
Если вы абсолютно ожидаете, что всегда будет значение, то используйте Optional::orElseThrow
чтобы выбросить исключение, если появляется нуль.
Если вы ожидаете появления нулевого значения и имеете альтернативный экземпляр, доступный как опция возврата, используйте опцию Optional::orElse
.
Если экземпляр fall-back не под рукой, но у вас есть функция вызова для предоставления экземпляра fall-back, используйте Optional::orElseGet
.
Если вы не хотите получать нуль и хотите ничего не делать, когда приходит нуль, используйте Optional::ifPresent
. Передайте блок кода, который будет запущен, если придет значение.
Если вам все равно, если приходит значение, удовлетворяющее некоторым требованиям, используйте параметр " Optional::filter
. Передайте Predicate
определяющий ваше требование. Например, мы заботимся только в том случае, если Optional< String >
содержит текст, а текст имеет в нем слово " purple
: myOptional.filter( s → s.contains( "purple" )).ifPresent( this::print ) ;
, Если получено null, наша желаемая операция (вызов для print
в этом примере) никогда не произойдет. Если значение было получено, но не удалось выполнить наш предикат, наша желаемая операция никогда не произойдет.
Выполнение if( myOptional.isPresent() ) { SomeClass x = myOptional.get(); … }
if( myOptional.isPresent() ) { SomeClass x = myOptional.get(); … }
является действительным и безопасным. Но это не оригинальное намерение Optional
поскольку оно в основном то же самое, что и старомодная нуль-проверка if ( null == x ) { … }
. Другие методы на Optional
обеспечивают более четкий и элегантный способ выразить ваши намерения относительно возможного нулевого поступления.
Ответ 4
вы также можете сделать:
Optional<ExpectedPackage> updatedPackage = expectedPackageRepository.findById(1).map(ep -> {
ep.setDateModified(new Date());
return expectedPackageRepository.saveAndFlush(ep);
});