Я наткнулся на следующий код:
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()
, а не использованию самого объекта.
@ Turing85 Turing85 это не NPE, вот что меня удивило.
использование obj
немного больше, чем использование x
Нет, я бы не получил NPE, поскольку карта вызывается только для объекта obj, который не равен нулю. Как вы предполагаете, лямбда странная - x -> x.doSomething()
имеет больше смысла.
Один создает замыкание, другой нет. Лучше всего использовать ссылку на метод. И лучше всего вообще не использовать опции для управления потоком. Необязательные параметры должны быть зарезервированы для возвращаемых значений методов, чтобы сигнализировать об отсутствии значения.
Вероятно, это ошибка/непреднамеренно, и подобные действия могут вызвать проблемы во время рефакторинга. Когда вы видите что-то подобное, вам, вероятно, следует заменить его чем-то, используя параметр лямбда (или ссылку на метод).
Например, вы можете решить, что хотите выполнить некоторые преобразования, возвращающие другой объект:
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 user85421 Кто-нибудь говорил, что это будет локальная переменная? Я ничего об этом не видел.
public void method(MyObject obj) { ...
Хороший улов. Я отредактировал свой ответ.
но я согласен, что использование x
было бы естественным способом сделать это (игнорируя использование необязательного параметра в качестве замены проверки на значение null)
как называется ошибка в коде, которая не может привести к заметному сбою во время выполнения, но которая, тем не менее, явно является ошибкой и вызовет головную боль в будущем? Потому что это то, что есть.
@rzwitserloot А как насчет «запаха кода»?
@ dan1st Это кажется более подходящим для уродливого, но намеренного кода. Возможно, это «намеренно» (в том смысле, что на момент написания именно это намеревался сделать автор, но с точки зрения стиля это очень плохая идея), но, скорее всего, не то, что автор намеревался сделать, но нет. test когда-нибудь обнаружит эту ошибку. сейчас. Однако позже это вызовет проблемы.
если
obj
являетсяnull
, тоobj.doSomething()
выкинет NPE. Нам следует использоватьx -> x.doSomething()
или, что еще лучше, использовать ссылку на метод:...map(MyObject::doSomething)...
.