Как стать автором
Обновить

Комментарии 12

На самом деле, возврат пустого массива для случая, когда header не проставлен, не до конца корректен.


Судя по спецификации Cookie header'а, он опционален. И даже приведен пример, когда его не будет.


Если запрос требует наличия Cookie, то отсутствие header'e говорит о том, что произошло нарушение контракта: мы ожидали header, а его нет. Это не то же самое, что и пустой header.


Условно, пустая парковка — это не то же самое, что и отсутствие парковки. Дом без свободных жилых квартир — это не то же самое, что и отсутствие дома.


В большинстве случаев действительно не так важно наличие header'а, как важно наличие необходимой Cookie. Т.е. для более-менее типичного приложения скорее важно то, что в запросе присутствует определенный корректный Cookie для авторизации, трекинга и пр. Но строго говоря, это верно не для всех программ. Более того, иногда было бы полезно ловить ситуации, что сервер не обрабатывал запрос не просто потому, что header содержит некорректные данные, а потому что такого header'а вообще нет (из-за блокировщика рекламы или из-за ошибки в реализации клиентской части).


Так что возврат null в том методе полностью обоснован, так как дополнительно передается информация о том, был ли вообще такой header.


Вы правы в другом — по состоянию на 2021 год мы уже изучили, что null — слишком опасен и коварен, так что грамотнее возвращать Either/Optional/sealed class/sealed interface и так далее, ибо в этом случае пользователю API придется рассмотреть все варианты. Более того, возврат массива тоже не до конца правилен (опять-таки, по состоянию на 2021 год), так как корректнее было бы возвращать ImmutableList, чтобы дать возможность получателю безопасно сохранить результат в поле класса, так как подобная коллекция гарантирует отсутствие изменений (а заодно само API могло бы в будущем кешировать результат, если это требуется). Но это по состоянию на 2021 год, а тот метод был введен еще до всех этих рассуждений.

Спасибо за ваше мнение. Это первая нормальная причина использования null в этом контексте. Тем не менее, я бы не сразу согласился.

Давайте посмотрим на HttpServletRequest как Java-объект. Если мы вызываем метод getCookies(), то мы как бы подразумеваем, что в этом объекте условно есть поле cookies. И с этой точки зрения мне кажется вполне допустимо возвращение пустого массива, так как куков нет (и не важно почему, их просто нет). По сути это означает, что данный объект не содержит куки.

Более того, иногда было бы полезно ловить ситуации, что сервер не обрабатывал запрос не просто потому, что header содержит некорректные данные, а потому что такого header'а вообще нет (из-за блокировщика рекламы или из-за ошибки в реализации клиентской части).


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

Вот листинг из Jetty:
if (cookies == null || cookies.getCookies().length == 0)    
	return null;
return _cookies.getCookies();


Если мы вызываем метод getCookies(), то мы как бы подразумеваем, что в этом объекте условно есть поле cookies.

Нет, так как в Java нет null safety. В новом C# оно есть, в Kotlin оно есть (частично), в Rust, а в Java нет. А потому любой аргумент может быть null'ом (если отдельно не оговорено в документации, и если реализация соответствует документации), результат вызова любой функции может быть null'ом и так далее. Кроме примитивный типов, разумеется.


Вот листинг из Jetty:

А вот в чем сакральный смысл работы Jetty мне непонятно. Может быть они боялись создавать пустой массив (хотя ведь его можно было бы закешировать в статической переменной). Мне кажется, что они нарушают контракт, так как не до конца корректно поняли, что надо возвращать.
Если же они правильно реализовали контракт, то смысла в возврате null'а я тоже не вижу.

В новом C# оно есть, в Kotlin оно есть (частично)

наоборот

В целом, в описанном случае какой смысл в null? У нас массив, он либо пустой, либо нет.
Если б мы возвращали обьект, тогда можно было бы о чем-то говорить, т.к. для такого случая пришлось бы создавать некий пустой/дефолтный обьект и проверять, а не пустой ли он, а это так или иначе приводит нас к доп. проверкам/странному поведению приложения

Я же написал — для более детального разбора ошибок. Представляя себя пользователем API, мне было бы намного удобнее, если бы программа в деталях писала, что было и чего не хватило, вместо фраз в стиле "запрос некорректен".
И потом я еще добавил, что в 2021 году использовать null для этого я считаю опасным, лучше всего возвращать sealed interface. В этом случае пользователь API может или рассмотреть все сценарии, или пойти по короткому пути и отреагировать только на ответ с наличием Cookies (а все остальное можно свести к ответу "нет искомой cookie"). Но по состоянию на конец 90х — начало 2000х разработчики API решили сэкономить и не использовать условные visitor'ы, ради простоты кода.


В любом случае, мы пошли по кругу. Мой аргумент про детализацию ошибок не является железобетонным, так что это скорее вкусовщина. И мы, вроде бы, сошлись во мнениях, что в современном коде null лучше не возвращать.

Представляя себя пользователем API, мне было бы намного удобнее, если бы программа в деталях писала, что было и чего не хватило, вместо фраз в стиле «запрос некорректен».

это уже вопрос обработки ошибок, имхо

У нас же ситуация простая — удалось получить куки или нет, зачем множить сущности
в 2021 году использовать null для этого я считаю опасным, лучше всего возвращать sealed interface.

Правильно, давайте заколачивать гвозди микроскопами. И LTS версии с поддержкой sealed classes ещё нет, на минуточку.

Если мы вызываем метод getCookies(), то мы как бы подразумеваем, что в этом объекте условно есть поле cookies.

Я не отрицаю что оно может быть null. Под есть я имел в виду просто наличие поля (условно и оно может быть null) (чтобы легче было представить как Java объект).

И полностью согласен с последним. Если Jetty имеет правильную реализацию (а скорее всего это так), то нет смысла возвращать null (точнее есть, но пустой массив был бы лучшей альтернативой)

'Условно, пустая парковка — это не то же самое, что и отсутствие парковки. Дом без свободных жилых квартир — это не то же самое, что и отсутствие дома'


Мне кажется аналогия не совсем удачная.
Если мне нужно проверить наличие конкретной cookie (или наличие конкретно авто во дворе), то мне не важно, вообще не пришли cookie или конкретно необходимой мне нет (неважно, нет парковки или машины, мне важен факт отсутствия)


Я согласен, что иногда null != empty list, но в случае с куками я не вижу кейса, где это разные ситуации.
Может Вы сможете пример привести какой-нибудь, когда необходимо отличить пустой список кук от их отсутствия?

Если мне нужно проверить наличие конкретной cookie (или наличие конкретно авто во дворе), то мне не важно, вообще не пришли cookie или конкретно необходимой мне нет (неважно, нет парковки или машины, мне важен факт отсутствия)

Я именно это и написал — в большинстве случаев это может быть неважно, так как я даже не посмотрю на этот слой, а буду настраивать условный Spring. Но вот для отладки было бы крайне полезно различать случаи, когда header вообще не проставляется, и когда отсутствует необходимая Cookie, так как в Http Client'е за это отвечают разные блоки кода (пользователь API ведь будет именно с этой стороны).


В худшем случае сервис авторизации (то есть тот, который использует класс HttpServletRequest) вернет ошибку "Auth Error", а дальше уже мне надо долго разбираться: что конкретно требовалось, где оно отсутствовало и так далее. Так как может быть все структуры заполняются, просто не передаются в Http Client. И вот для высокой детализации ошибки я бы и хотел получать максимум информации, чтобы исключить сценарий, когда каждый слой приложения теряет чуть-чуть деталей, так что потом необходимо долго изучать проблему, так как одно и то же поведение вызывается разным внешним воздействием.

В java очень не хватает простой проверки на null c возможностью образования цепочек проверки, что-то типа?.., как в котлине
На самом деле есть. Чуть более многословно, но, лично для меня, гораздо удобнее.

Optional.ofNullable()
.map() //по желанию
.map()
.orElseGet\Throw()
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории