Необязательный 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);
});