Необязательный вариант для исключения исключения

Верно ли, что, поскольку Java 1.8, возвращающий объект Optional, предпочтительнее, чем выброс исключения? Все чаще я вижу код следующим образом:

  public Optional<?> get(int i) {
        // do somtething
        Object result = ...
        Optional.ofNullable(result);
    }

Вместо этого:

public Object get(int i) {
        if(i<0 || i>=size) {
            throw new IndexOutOfBoundsException("Index: " + i + ". Size: " + size);
        }
        // do somtething
        Object result = ...
        return result;
    }

Это означает, что нам нужно забыть старый подход и использовать новые? А где Optional подходит вообще?

Ответы

Ответ 1

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

Код, который вы представляете как "старая идиома", выполняет проверку ввода и выдает исключение, если вход недействителен. Это поведение должно оставаться неизменным, даже если вы вводите опцию. Единственное различие заключается в том, что возвращаемое значение Object get(i) возможно равно нулю, тогда как возвращаемое значение Optional<?> get(i) никогда не является нулевым, поскольку существует специальное состояние экземпляра Option, представляющего отсутствие значения.

Преимущество методов, которые возвращают Необязательный вместо значения NULL, - это устранение кода шаблона, который должен выполнить обычную проверку нулевого уровня, прежде чем пытаться что-либо сделать с возвращаемым значением. Есть гораздо больше преимуществ в использовании Необязательно исключительно внутри метода. Например:

static Optional<Type> componentType(Type type) {
  return Optional.of(type)
                 .filter(t -> t instanceof ParameterizedType)
                 .map(t -> (ParameterizedType) t)
                 .filter(t -> t.getActualTypeArguments().length == 1)
                 .filter(t -> Optional.of(t.getRawType())
                                      .filter(rt -> rt instanceof Class)
                                      .map(rt -> (Class<?>) rt)
                                      .filter(Stream.class::isAssignableFrom)
                                      .isPresent())
                 .map(t -> t.getActualTypeArguments()[0]);

Здесь важное преимущество - прекрасный контроль над областью: одно и то же имя t повторно используется в каждой новой области для переменной типа, подходящего для этого этапа обработки. Таким образом, вместо того, чтобы быть вынужденным иметь переменные по охвату после истечения срока их полезного использования и изобретать новое имя для каждой следующей переменной, с этой идиомой мы имеем точный минимум, который нам нужно продолжить.

Просто для интереса вы можете реализовать equals полностью в терминах Необязательно:

@Override public boolean equals(Object obj) {
  return Optional.ofNullable(obj)
                 .filter(that -> that instanceof Test)
                 .map(that -> (Test)that)
                 .filter(that -> Objects.equals(this.s1, that.s1))
                 .filter(that -> Objects.equals(this.s2, that.s2))
                 .isPresent();
}

Хотя я считаю эту идиому очень чистой и удобочитаемой, она в настоящее время не оптимизирована, чтобы ее можно было рекомендовать как достойный выбор. Однако будущие версии Java могут сделать это жизнеспособным.

Ответ 2

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

Поскольку Опция является новой, существует также тенденция к ее чрезмерному использованию; мы надеемся, что со временем правильные шаблоны будут усвоены.

Необязательный - пример шаблона нулевого объекта; он обеспечивает безопасный способ сказать "ничего не было", когда "ничего нет" - разумный ожидаемый результат. (Возвращение пустого массива или пустой коллекции - это аналогичные примеры в этих доменах.) Если вы хотите представлять "ничего там" с помощью null/optional vs exception, как правило, является функцией того, является ли "ничего там" обычно ожидаемой ситуацией, или является ли это исключительным. Например, никто не хочет, чтобы Map.get выдавал исключение, если отображение отсутствует; map-not-present - ожидаемый, а не исключительный результат. (Если бы мы имели Optional в 1997 году, Map.get мог бы вернуть Optional.)

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

Ответ 3

В ситуациях, где могут возникнуть ошибки, подходящим типом данных является Try.

Вместо использования абстракций "present" или "empty", Try использует абстракции "отказ" или "успех".

Поскольку Try не предоставляется Java 8 из коробки, необходимо использовать некоторую 3. партийную библиотеку. (Может быть, мы увидим, что он добавлен в Java 9?)

Попробовать Java