Статьи

Соображения при возврате Java 8 необязательно из метода

Класс Optional, представленный в Java 8, был одной из самых противоречивых функций, представленных в этой версии языка. Хотя мне нравится больше вещей об этом новом классе Java, чем мне не нравится, есть несколько вещей, которые следует учитывать при использовании его в качестве return типа в методах Java. Я обсуждаю некоторые из них в этом посте, но не обсуждаю спор о том, следует ли ограничивать использование Optional в качестве возвращаемого типа. Я также предполагаю, что Optional используется только в качестве возвращаемого типа, когда ожидается, что в некоторых случаях этот метод не должен иметь значения для возврата. Наконец, эти наблюдения применимы и к другим типам, и к непосредственному использованию null в Java, но Optional подчеркивает и конкретно иллюстрирует эти наблюдения.

Одиночные и множественные возвраты

В общем сообществе разработчиков программного обеспечения и сообществе разработчиков Java в течение некоторого времени велась дискуссия (« религиозная война ») о том, следует ли писать методы для return только один раз (не считая исключения в этом обсуждении). С одной стороны, Егор Бугаенко утверждает, что « многие операторы возврата являются плохой идеей в ООП », Том Даллинг утверждает, что « иметь одну точку выхода (возврата) из функции — это хорошо », и многие утверждают, что несколько операторов return часто указать необходимость этого метода для рефакторинга . С другой стороны, Брюс Экель утверждает, что множественные операторы return могут сделать код «более понятным», Тейлор Готье утверждает, что « максима » «Метод должен иметь одну и только одну точку выхода», «не может быть более неправильным», Питер Ричи утверждает, что строгое соблюдение единственного выхода может привести к тому, что сегодня код будет «менее читабелен» на «объектно-ориентированных языках», а Марк Левисон обрисовывает в общих чертах « некоторые причины, по которым мне не нравится аргумент единого выхода ».

В посте « Множественные возвратные операторы » Николай Парлог пишет об истории и соображениях, которые необходимо сделать в связи с идеей метода, возвращаемого только один раз. Он включает в себя раздел «Ситуации для множественных отчетов о возвратах», в котором он описывает «несколько видов ситуаций, в которых метод может извлечь выгоду из множественных отчетов о доходах». Мое лучшее предположение состоит в том, что многие разработчики чувствуют то же, что и я, а именно, что «это зависит» при принятии решения, должен ли конкретный метод иметь только один оператор return или должен иметь более одного оператора return .

Поскольку я начал чаще использовать Optional в Java 8 для возвращаемых типов моих методов, я обнаружил, что использование Optional в качестве возвращаемого типа является еще одним соображением, которое необходимо учитывать при принятии решения о возврате из метода один или несколько раз.

Когда объявляется, что метод Java возвращает Optional , важно полностью понимать, что это не мешает разработчику, пишущему этот метод, возвращать null . Возвращаемый Optional является ссылочным типом и, как и любой ссылочный тип, может иметь значение null . Крайне важно, чтобы разработчик, пишущий метод, который возвращает Optional НИКОГДА не должен иметь этот метод, возвращающий null [ Optional.empty () обычно должен возвращаться вместо этого]. Я собираюсь повторить этот пункт с двумя цитатами:

  • Выделенное предложение из пункта № 55 в Effective Java , третье издание : « Никогда не возвращайте нулевое значение из метода Optional возврата. «
  • Правило № 1 Стюарта Маркса об использовании Optional : «Никогда, никогда не используйте null для необязательной переменной или возвращаемого значения».

Один из аргументов против нескольких операторов return в методе заключается в том, что он усложняет распознавание того, что возвращается в каждом случае (чтобы найти все возможные сценарии возврата). Использование Optional в качестве типа возврата является конкретным примером, иллюстрирующим это. Хотелось бы убедиться, что в некоторых случаях метод не возвращает null а в других случаях Optional экземпляр. Компилятору, конечно, все равно, что возвращается в каждом случае.

Один из подходов к решению этой проблемы состоит в том, чтобы возвращаться из метода только один раз, и тогда разработчик, пишущий код, и разработчик, проверяющий код, могут легко убедиться, что значение null не возвращено. Этим разработчикам нужно искать только вызов Optional.of (T), вызов Optional.ofNullable (T) или вызов Optional.empty () .

Работа с локальной переменной базового типа данных в методе

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

Примеры объявления локальной переменной, которая в конечном итоге возвращается как необязательная

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * Provides the middle name if it exists.
 *
 * @return Middle name if it exists or empty if it doesn't exist.
 */
public Optional<String> determineMiddleName1()
{
   String middleName;
   // Do whatever logic is necessary
   return Optional.ofNullable(middleName);
}
 
/**
 * Provides the middle name if it exists.
 *
 * @return Middle name if it exists or empty if it doesn't exist.
 */
public Optional<String> determineMiddleName2()
{
   Optional<String> middleName;
   // Do whatever logic is necessary
   return middleName;
}

В приведенном выше коде метод determineMiddleName 1 () работает с локальной переменной базового типа. Это обычно легче установить / заполнить, чем Optional и использование Optional.isNullable() в конце гарантирует, что даже null middleName будет возвращено как «пустое» Optional вместо null .

Метод determineMiddleName 2 () в вышеприведенном коде объявляет свою локальную переменную, которая в конечном итоге будет возвращена как Optional<String> а затем возвращает эту ссылку в конце метода.

Избегайте инициализации локальной переменной по умолчанию

Поскольку метод determineMiddleName 2 () написан выше, компилятор поможет убедиться, что локальная переменная «middleName» установлена ​​на что-то (даже если это «что-то» равно null ), но разработчик выбрал инициализацию этой переменной «middleName» для null , компилятор не будет иметь проблем с этим. Если локальная переменная должна быть инициализирована по какой-либо причине, было бы лучше инициализировать ее в Optional.empty() вместо null . Если бы разработчик решил инициализировать эту переменную с помощью Optional.empty() , для второго примера все еще возможно «сбросить» локальную переменную на null позже в методе.

Это обсуждение приводит меня к трем несомненным замечаниям относительно использования Optional в качестве типа возврата метода в Java.

  1. Влияние одинарного или множественного return на возможность случайного возврата null вместо «пустого» или другого null Optional ссылка должна учитываться при принятии решения о том, имеет ли смысл одиночный или множественный возврат.
  2. Часто будет легче обеспечить возврат ссылки Optional , чем возвращение null , работая с базовым типом во всем методе и создавая экземпляр возвращаемого Optional в самый последний момент (обычно при его return ).
  3. Локальная переменная типа Optional которая в конечном итоге должна быть возвращена из метода, НИКОГДА не должна изначально присваиваться null даже если «известно», что она будет переустановлена ​​соответствующим образом. Может быть, лучше вообще не определять его, чтобы компилятор обеспечивал его установку в каждой «ветви» потока кода. По крайней мере, если она должна быть инициализирована, локальная переменная типа Optional должна быть инициализирована как Optional.empty() а не как null .

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

Пример избегания возврата нуля с множественными операторами возврата

01
02
03
04
05
06
07
08
09
10
11
public Optional<String> getGuardedData(final String input)
{
   if (input == null)
   {
      return Optional.empty();
   }
 
   String data;
   // Do whatever logic is necessary
   return Optional.ofNullable(data);
}

Я обнаружил, что класс Optional , при правильном использовании в качестве типа возврата метода, может сделать код клиента более читабельным из-за его большей беглости. Однако для достижения максимального значения Optional должен применяться с такой дисциплиной, чтобы клиенты кода могли ожидать, что возвращаемый Optional никогда не будет null . В этом посте рассмотрены некоторые соображения, которые могут помочь гарантировать, что значение null никогда не будет возвращено методом, который объявляет себя как возвращающий Optional . Без уверенности в том, что метод никогда не вернет null , использование Optional в качестве возвращаемого типа только ухудшает ситуацию, поскольку вынуждает клиента сначала проверять null Optional прежде чем вызывать один из методов этого Optional . Это делает вызывающий код менее беглым .

Опубликовано на Java Code Geeks с разрешения Дастина Маркса, партнера нашей программы JCG . См. Оригинальную статью здесь: Соображения при возврате Java 8 необязательно из метода

Мнения, высказанные участниками Java Code Geeks, являются их собственными.