Почему в Optional не срабатывает orElse(), orElseGet(), если объект null?

Рейтинг: 1Ответов: 1Опубликовано: 06.04.2023
AggregationDescription aggregationLevel = getAggregationDescriptionBYTerm(LEVEL_FIELD, null, null);

Есть функция:

private AggregationDescription getAggregationDescriptionBYTerm(String term, Optional<String> subTerm, Optional<String> script){

    AggregationDescription aggregationDescription = subTerm.map(term2 -> {
        if(script != null){
            return AggregationDescription.builder().term("by_" + term).field(term)
                    .subAggregationDescription(Arrays.asList(AggregationDescription.builder().term("by_" + term2).field(term2)
                                    .script(script.get()).build())).build();
        }
         return AggregationDescription.builder().term("by_" + term).field(term)
                .subAggregationDescription(Arrays.asList(AggregationDescription.builder().term("by_" + term2).field(term2)
                        .build())).build();
    }).orElseGet( () -> script.map(scrpt ->{
        return AggregationDescription.builder().term("by_" + term).script(scrpt).field(term).build();})
            .orElseGet(() -> AggregationDescription.builder().term("by_" + term).field(term).build()));

    return aggregationDescription;
}

она падает сразу с NullPointException на строчке:

AggregationDescription aggregationDescription = subTerm.map(term2 -> {

если 2-ой параметр Optional String subTerm == null. Ожидал, что в этом случае будет выполнен код из orElseGet(), но этого не происходит

Ответы

▲ 3Принят

Вы буквально передаете null, пустую ссылку. У null нет никаких методов от Optional (вообще никаких методов нет), попытка вызвать любой метод от null вызовет NPE. То что вы указали тип аргумента Optional, не означает, что туда можно безболезненно null передавать, и он автоматом превратится в Optional.

Либо не передавайте null, оборачивайте в Optional или передавайте явно Optional.empty():

AggregationDescription aggregationLevel = getAggregationDescriptionBYTerm(LEVEL_FIELD,
    Optional.empty(), Optional.empty());

или уже внутри функции заменяйте null на Optional.empty() (выглядит не очень хорошим вариантом):

if (subTerm == null) subTerm = Optional.empty();
if (script == null) script = Optional.empty();

или просто передавайте через параметры обычный String, а внутри уже оборачивайте в Optional:

private AggregationDescription getAggregationDescriptionBYTerm(String term, String subTerm, String script){
    Optional<String> optionalSubterm = Optional.ofNullable(subTerm);
    Optional<String> optionalScript = Optional.ofNullable(script);

    AggregationDescription aggregationDescription = optionalSubterm.map(term2 -> {
        if(script != null){
            return AggregationDescription.builder().term("by_" + term).field(term)
                    .subAggregationDescription(Arrays.asList(AggregationDescription.builder().term("by_" + term2).field(term2)
                            .script(script).build())).build();
        }
        return AggregationDescription.builder().term("by_" + term).field(term)
                .subAggregationDescription(Arrays.asList(AggregationDescription.builder().term("by_" + term2).field(term2)
                        .build())).build();
    }).orElseGet( () -> optionalScript.map(scrpt ->{
                return AggregationDescription.builder().term("by_" + term).script(scrpt).field(term).build();})
            .orElseGet(() -> AggregationDescription.builder().term("by_" + term).field(term).build()));

    return aggregationDescription;
}

Фрагмент начиная с if(script != null){ по-хорошему стоило бы переделать на что-то вида

return optionalScript.map(script -> ...).orElseGet(...);