Должен ли Java 8 getters возвращать необязательный тип?
Optional
тип, введенный в Java 8, является новой вещью для многих разработчиков.
- Это метод получения возврата Optional<Foo>
введите вместо классического Foo
хорошая практика? Предположим, что значение может быть null
.
5 ответов
конечно, люди будут делать то, что они хотят. Но у нас было четкое намерение при добавлении этой функции, и это было не быть общего назначения, возможно, типа, столько, сколько многие люди хотели бы, чтобы мы это сделали. Наше намерение состояло в том, чтобы предоставить ограниченный механизм для типов возврата метода библиотеки, где должен быть четкий способ представления "нет результата" и использование null
для таких было подавляюще вероятно вызвать ошибки.
например, вы, вероятно, никогда не следует использовать его для того, что возвращает массив результатов или список результатов; вместо этого верните пустой массив или список. Вы почти никогда не должны использовать его в качестве поля чего-либо или параметра метода.
Я думаю, что регулярное использование его в качестве возвращаемого значения для геттеров определенно будет чрезмерным.
ничего неправильно С необязательным, что его следует избегать, это просто не то, что многие люди хотят, чтобы это было, и, соответственно, мы были справедливо обеспокоены риском чрезмерного использования.
(публичное объявление: никогда вызов Optional.get
Если вы не можете доказать, что он никогда не будет нулевым; вместо этого используйте один из безопасных методов, таких как orElse
или ifPresent
. Оглядываясь назад, мы должны были позвонить get
что-то вроде getOrElseThrowNoSuchElementException
или что-то, что сделало гораздо яснее, что это был очень опасный метод, который подрывал всю цель Optional
в первую очередь. Урок. (Обновление: Java 10 имеет Optional.orElseThrow()
, что семантически эквивалентно get()
, но чье имя более подходит.))
после проведения собственных исследований я наткнулся на ряд вещей, которые могут подсказать, когда это уместно. Наиболее авторитетной является следующая цитата из статьи Oracle:
" важно отметить, что намерение Факультативного класса не заменять каждую пустую ссылку. Вместо этого, его цель состоит в том, чтобы помочь design более понятный APIs так что, просто прочитав подпись способ, вы можете сказать, можно ли ожидать необязательное значение. Это заставляет вас активно разворачивать необязательное, чтобы справиться с отсутствием значения." - устали от исключений нулевого указателя? Рассмотрите возможность использования Java SE 8 по желанию!
Я нашел этот отрывок из Java 8 необязательно: как его использовать
"необязательный не предназначен для использования в этих контекстах, так как он ничего не купит нам:
- в слое модели домена (не сериализуемый)
- в DTOs (по той же причине)
- во входных параметрах методов
- в параметрах конструктора"
который также, кажется, поднимает некоторые допустимые точки.
Я не смог найти никаких негативных коннотаций или красных флагов, чтобы предположить, что Optional
следует избегать. Я думаю, генерал идея в том, что если это полезно или улучшает удобство использования вашего API, используйте его.
Я бы сказал, что в целом это хорошая идея использовать необязательный тип для возвращаемых значений, которые могут быть nullable. Однако, w.r.т. для фреймворков я предполагаю, что замена классических геттеров необязательными типами вызовет много проблем при работе с фреймворками (например, Hibernate), которые полагаются на соглашения кодирования для геттеров и сеттеров.
причина Optional
был добавлен в Java, потому что это:
return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
.stream()
.filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
.filter(m -> Arrays.equals(m.getParameterTypes(), parameterClasses))
.filter(m -> Objects.equals(m.getReturnType(), returnType))
.findFirst()
.getOrThrow(() -> new InternalError(...));
чище, чем этот:
Method matching =
Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
.stream()
.filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
.filter(m -> Arrays.equals(m.getParameterTypes(), parameterClasses))
.filter(m -> Objects.equals(m.getReturnType(), returnType))
.getFirst();
if (matching == null)
throw new InternalError("Enclosing method not found");
return matching;
Я хочу сказать, что необязательно было написано Для поддержки функционального программирования, который был добавлен в Java одновременно. (Пример приходит любезно блог Брайана Гетца. Лучший пример может использовать orElse()
метод, так как этот код будет вызывать исключение в любом случае, но вы получите картину.)
но теперь, люди используют Optional по совсем другой причине. Они используют его, чтобы устранить изъян в языковом дизайне. Недостаток заключается в следующем: невозможно указать, какие из параметров API и возвращаемых значений могут быть null. Это может быть упомянуто в javadocs, но большинство разработчиков даже не пишут javadocs для своего кода, и не многие будут проверять javadocs, когда они пишут. Таким образом, это приводит к большому количеству кода, который всегда проверяет значения null перед их использованием, хотя они часто не могут возможно, null, потому что они уже были проверены повторно девять или десять раз в стеке вызовов.
Я думаю, что была настоящая жажда решить этот недостаток, потому что так много людей, которые видели новый факультативный класс, предполагали, что его цель-добавить ясность к APIs. Вот почему люди задают такие вопросы как "геттеры должны возвращать опции?- Нет, вероятно, не должны, если только вы не ожидаете, что геттер будет использоваться в функциональном программировании, что очень маловероятно. На самом деле, если вы посмотрите, где Необязательный используется в Java API, это в основном в потоковых классах, которые являются ядром функционального программирования. (Я не проверил очень тщательно, но классы Stream могут быть только место, где они используются.)
если вы планируете использовать геттер в немного функциональном коде, было бы неплохо иметь стандартный геттер и второй, который возвращает необязательный.
О, и если вам нужно, чтобы ваш класс был сериализуемым, вы абсолютно не должны использовать Необязательный.
Optionals-очень плохое решение проблемы API, потому что a) они очень многословны, и b) они никогда не предназначались для решения этой проблемы в первую очередь.
гораздо лучшим решением проблемы API является Nullness Checker. Это обработчик аннотаций, который позволяет указать, какие параметры и возвращаемые значения могут быть нулевыми, аннотируя их с помощью @Nullable. Таким образом, компилятор может сканировать код и выяснить, является ли значение это может быть null передается в значение, где null не допускается. По умолчанию предполагается, что ничто не может быть null, если оно не аннотировано таким образом. Таким образом, вам не нужно беспокоиться о значениях null. Передача значения null параметру приведет к ошибке компилятора. Тестирование объекта на null, которое не может быть null, создает предупреждение компилятора. Результатом этого является изменение NullPointerException из ошибки времени выполнения в ошибку времени компиляции.
это меняет всё.
что касается ваших геттеров, не используйте необязательный. И попробуйте создать свои классы, чтобы ни один из членов не мог быть нулевым. И, возможно, попробуйте добавить Nullness Checker в свой проект и объявить ваши геттеры и параметры setter @Nullable, если им это нужно. Я делал это только с новыми проектами. Вероятно, он создает много предупреждений в существующих проектах, написанных с большим количеством лишних тестов для null, поэтому его может быть сложно модифицировать. Но он также будет ловить много жуки. Я люблю это. Из-за этого мой код намного чище и надежнее.
(существует также новый язык, который обращается. Kotlin, который компилируется в байтовый код Java, позволяет указать, может ли объект быть нулевым при его объявлении. Это более чистый подход.)
добавление к оригинальному сообщению (версия 2)
после того, как я много думал, я неохотно пришел к выводу, что приемлемо возвращать необязательный на одном условие: полученное значение может быть нулем. Я видел много кода, где люди обычно возвращают необязательный из геттеров, которые не могут вернуть null. Я рассматриваю это как очень плохую практику кодирования, которая только добавляет сложности коду, что делает ошибки более вероятными. Но когда возвращаемое значение может быть null, продолжайте и оберните его внутри необязательного.
имейте в виду, что методы, предназначенные для функционального программирования, и которые требуют функции ссылка, будет (и должна) быть написана в двух формах, одна из которых использует необязательно. Например, Optional.map()
и Optional.flatMap()
оба берут ссылки на функции. Первый принимает ссылку на обычный геттер, а второй принимает тот, который возвращает необязательный. Таким образом, вы никому не делаете одолжение, возвращая необязательное значение, где значение не может быть null.
сказав Все это, я все еще вижу подход, используемый Nullness Checker - это лучший способ справиться с нулями, так как они поворачивают NullPointerExceptions из ошибок выполнения для ошибок времени компиляции.
если вы используете современные сериализаторы и другие фреймворки, которые понимают Optional
тогда я нашел эти рекомендации хорошо работать при написании Entity
бобы и доменные слои:
- если уровень сериализации (обычно DB) позволяет
null
значение ячейки в столбцеBAR
в таблицеFOO
, то геттерFoo.getBar()
может возвратитьOptional
указание разработчику, что это значение может разумно ожидаться как null и они должны разобраться с этим. Если DB гарантирует, что значение не будет null, то геттер должен не обернуть вOptional
. -
Foo.bar
должно бытьprivate
и не бытьOptional
. На самом деле нет причин для этогоOptional
Если этоprivate
. - сеттер
Foo.setBar(String bar)
следует взять типbar
и неOptional
. Если это нормально использоватьnull
аргумент затем указывает это в комментарии JavaDoc. Если это не нормально использоватьnull
anIllegalArgumentException
или какая-то соответствующая бизнес-логика, ИМХО, более уместна. - конструкторам не нужно
Optional
аргументы (по причинам, аналогичным пункту 3). Обычно я включаю в конструктор только аргументы, которые должны иметь значение null в базе данных сериализации.
чтобы сделать вышеуказанное более эффективным, вы можете отредактировать шаблоны IDE для генерации геттеров и соответствующих шаблонов для toString()
, equals(Obj o)
etc. или используйте поля непосредственно для них (большинство генераторов IDE уже имеют дело с нулями).