Использование объектов, допускающих значение NULL, в дополнительных методах после покрытия их с помощью ofNullable

Я наткнулся на следующий код:

public void method(MyObject obj) {
    String value = Optional.ofNullable(obj)
                           .map(x -> obj.doSomething())
                           .orElse(MyObject.DEFAULT_VALUE);

}

Мне интересно узнать, как используется x -> obj.doSomething() вместо x -> x.doSomething() в методе map.

Здесь, даже если obj равно null, NullPointerException НЕ будет выброшено, потому что мы вызываем метод ofNullable перед map и, следовательно, функция сопоставления будет вызываться только тогда, когда obj не равно нулю.

Таким образом, с точки зрения результатов, и x -> obj.doSomething(), и x -> x.doSomething() будут эквивалентны в случае Optional. (Очевидно, что результат будет другим в случае Stream или при использовании Optional.of).

Есть ли другие различия? И с точки зрения использования, я думаю, следует отдать предпочтение x -> x.doSomething(), а не использованию самого объекта.

если objявляется null, то obj.doSomething() выкинет NPE. Нам следует использовать x -> x.doSomething() или, что еще лучше, использовать ссылку на метод: ...map(MyObject::doSomething)....

Turing85 26.06.2024 14:47

@ Turing85 Turing85 это не NPE, вот что меня удивило.

Gautham M 26.06.2024 14:50

использование obj немного больше, чем использование x

user85421 26.06.2024 14:53

Нет, я бы не получил NPE, поскольку карта вызывается только для объекта obj, который не равен нулю. Как вы предполагаете, лямбда странная - x -> x.doSomething() имеет больше смысла.

GrumpyWelshGit 26.06.2024 14:53

Один создает замыкание, другой нет. Лучше всего использовать ссылку на метод. И лучше всего вообще не использовать опции для управления потоком. Необязательные параметры должны быть зарезервированы для возвращаемых значений методов, чтобы сигнализировать об отсутствии значения.

knittl 26.06.2024 14:54
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
5
80
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вероятно, это ошибка/непреднамеренно, и подобные действия могут вызвать проблемы во время рефакторинга. Когда вы видите что-то подобное, вам, вероятно, следует заменить его чем-то, используя параметр лямбда (или ссылку на метод).

Например, вы можете решить, что хотите выполнить некоторые преобразования, возвращающие другой объект:

String value = Optional.ofNullable(obj)
   .map(x -> createOtherObjectOfSameTypeForFurtherProcessing(x))
   .map(x -> obj.doSomething())
   .orElse(MyObject.DEFAULT_VALUE);

В этом случае obj.doSomething() по-прежнему будет работать со старым объектом.

В качестве альтернативы, если obj на самом деле является полем, а не параметром или локальной переменной, как в вашем примере (спасибо user85421 в комментариях за указание, что в этом примере это не так), возможно, что obj есть изменено перед вызовом map:

//WARNING: questionable code
String value = Optional.ofNullable(obj)
   .filter(x -> {
      obj = null;//weird stuff done here, probably better don't do that in production
      return true;
   })
   .map(x -> obj.doSomething())
   .orElse(MyObject.DEFAULT_VALUE);

Нечто подобное может произойти и с другим потоком (что (не всегда) может быть разумным, если obj является, например, неизменяемым и volatile)

И поскольку obj является параметром в вашем примере, вы также не можете использовать свой код, если obj был изменен в какой-то момент (как также указано в этом комментарии), ваш код не будет компилироваться.

//DOES NOT COMPILE
public void method(MyObject obj) {
    if (someEdgeCase()){
        obj = null;//makes obj not effectively final
    }
    String value = Optional.ofNullable(obj)
       .map(x -> obj.doSomething())//compiler error because obj is not effectively final
       .orElse(MyObject.DEFAULT_VALUE);

}

но obj должен быть окончательным или фактически окончательным — его нельзя изменить до вызова map

user85421 26.06.2024 14:54

@user85421 user85421 Кто-нибудь говорил, что это будет локальная переменная? Я ничего об этом не видел.

dan1st 26.06.2024 14:55
public void method(MyObject obj) { ...
user85421 26.06.2024 14:55

Хороший улов. Я отредактировал свой ответ.

dan1st 26.06.2024 14:57

но я согласен, что использование x было бы естественным способом сделать это (игнорируя использование необязательного параметра в качестве замены проверки на значение null)

user85421 26.06.2024 15:00

как называется ошибка в коде, которая не может привести к заметному сбою во время выполнения, но которая, тем не менее, явно является ошибкой и вызовет головную боль в будущем? Потому что это то, что есть.

rzwitserloot 26.06.2024 15:01

@rzwitserloot А как насчет «запаха кода»?

dan1st 26.06.2024 15:05

@ dan1st Это кажется более подходящим для уродливого, но намеренного кода. Возможно, это «намеренно» (в том смысле, что на момент написания именно это намеревался сделать автор, но с точки зрения стиля это очень плохая идея), но, скорее всего, не то, что автор намеревался сделать, но нет. test когда-нибудь обнаружит эту ошибку. сейчас. Однако позже это вызовет проблемы.

rzwitserloot 26.06.2024 16:35

Другие вопросы по теме