Должен ли 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 бобы и доменные слои:

  1. если уровень сериализации (обычно DB) позволяет null значение ячейки в столбце BAR в таблице FOO, то геттер Foo.getBar() может возвратить Optional указание разработчику, что это значение может разумно ожидаться как null и они должны разобраться с этим. Если DB гарантирует, что значение не будет null, то геттер должен не обернуть в Optional.
  2. Foo.bar должно быть private и не быть Optional. На самом деле нет причин для этого Optional Если это private.
  3. сеттер Foo.setBar(String bar) следует взять тип bar и не Optional. Если это нормально использовать null аргумент затем указывает это в комментарии JavaDoc. Если это не нормально использовать null an IllegalArgumentException или какая-то соответствующая бизнес-логика, ИМХО, более уместна.
  4. конструкторам не нужно Optional аргументы (по причинам, аналогичным пункту 3). Обычно я включаю в конструктор только аргументы, которые должны иметь значение null в базе данных сериализации.

чтобы сделать вышеуказанное более эффективным, вы можете отредактировать шаблоны IDE для генерации геттеров и соответствующих шаблонов для toString(), equals(Obj o) etc. или используйте поля непосредственно для них (большинство генераторов IDE уже имеют дело с нулями).