Почему методы Jave не возвращают кортеж вместо ссылки на объект (или null)?

#java #methods #null #tuples

#java #методы #null #кортежи

Вопрос:

Обычно методы Java выглядят следующим образом:

 public <U,V> U doSomething(V aReference) {
    // Do something
}
  

Обычно это означает, что метод doSomething() возвращает null , если он
сбой (по какой-либо причине) или допустимая ссылка на объект. В некоторых случаях
«допустимая ссылка на объект» сама по себе может быть null . Например, метод
aMap.get(k) может возвращать null , если нет ключа k или если есть ключ k , но его соответствующее значение равно null . Путаница!

Не говоря уже о NullPointerException s, если 50% вашего LOC не просто проверяет нуль.

Что плохого в методах, выглядящих следующим образом:

 public <T> ReturnTuple<T> doSomething(V aReference) {
    T anotherObjRef = getValidObjT();
    if (successful) {
        return ReturnTuple.getSuccessTuple(anotherObjRef);
    } else {
        return ReturnTuple.getFailureTuple("aReference can't be null");
    }
}
  

где класс ReturnTuple<T> определяется примерно так:

 class ReturnTuple<T> {
    private boolean success;

    // Read only if success == true
    private T returnValue;

    // Read only if success == false
    private String failureReason;

    // Private constructors, getters, setters amp; other convenience methods

    public static <T> ReturnTuple<T> getSuccessTuple(T retVal) {
        // This code is trivial
    }

    public static <T> ReturnTuple<T> getFailureTuple(String failureReason) {
        // This code is trivial
    }
}
  

Тогда вызывающий код будет выглядеть следующим образом:

 ReturnTuple<T> rt = doSomething(v);
if (rt.isSuccess()) {
    // yay!
} else {
    // boo hoo!
}
  

Итак, мой вопрос: почему этот шаблон не является более распространенным? Что с этим не так?

Имейте в виду, я прошу критики не этого конкретного кода, а этой общей идеи.

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

Правка 1: мотивация

Думаю, мне следовало добавить этот раздел с самого начала, но лучше поздно, чем никогда…

  • Вы когда-нибудь хотели, чтобы метод мог возвращать два значения одновременно? Или что возврат значения может быть не связан с возможностью указывать успех или неудачу?

  • Это также могло бы продвинуть идею о том, что метод является аккуратным автономным модулем (с низкой связью и высокой когезией): обрабатывает все (или большинство) исключений, сгенерированных во время его выполнения (не говоря об исключениях типа IllegalArgumentException ), незаметно регистрирует причины сбоя (а не уродливую трассировку стека неперехваченного исключения) и предоставляет вызывающему только ту информацию, которая требуется. ИМХО, это также способствует сокрытию информации и инкапсуляции.

  • Вы сделали все возможное при тестировании, но когда код развертывается для клиента, из-за уродливой трассировки стека из-за неперехваченного исключения все это выглядит непрофессионально.

  • Аналогично пункту выше: у вас может быть код, который, возможно, мог бы генерировать 20 различных исключений, но вы перехватываете только 5-7 из них. Как мы все знаем, клиенты делают ужасные вещи: полагаются на них, чтобы вызвать все остальные неперехваченные 13-15 исключений :-). В конечном итоге вы выглядите плохо, когда они видят большую трассировку стека (вместо отдельной причины сбоя, добавленной в журналы).

    В этом разница (например) между показом трассировки стека пользователю в веб-приложении и показом им красиво отформатированной страницы с ошибкой 5xx, на которой говорится что-то вроде: «Произошла ошибка, и ваш запрос не удалось выполнить. Администраторы были уведомлены и исправят как можно скорее. «и т.д.

Эта идея не совсем лишена смысла, поскольку Java 8 предоставляет необязательный класс (как указано @JBNizet), а в библиотеке Google Guava также есть необязательный класс. Это просто заходит немного дальше.

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

1. Зачем тратить столько времени только для того, чтобы получить if(bla.isSuccess()) сравнение с if(bla != null) ?

2. Поскольку возврат null вызывает путаницу: это не всегда может означать сбой, это может быть подлинное успешное значение (см. Map Пример выше). Кроме того, вызываемый метод может перехватывать исключения (при желании) и просто указывать на сбой вместе с сообщением о сбое.

3. И map также может просто вызывать ContainsKey() . Я не понимаю вашей точки зрения по поводу исключений: как это влияет на что-либо из этого?

4. @TAsk это не вопрос накладных расходов, это вопрос возможности а) четко указывать успех или неудачу и б) в случае неудачи одновременно возвращать причину сбоя.

5. @JeroenVannevel Традиционный способ Java — создавать и перехватывать исключения повсюду. Вместо этого каждый метод может быть автономным (способствуя низкой связи) и просто указывать на успех или неудачу. Разве вы не видели в своем коде experience, где неперехваченные исключения отображаются только при развертывании на клиенте? Выглядит так некрасиво.

Ответ №1:

Обычно это означает, что метод doSomething() возвращает null в случае сбоя

Нет, это не значит, что. Это означает, что метод doSomething() иногда может законно возвращать null данные без сбоев. Java предоставляет мощную систему для обработки сбоев, а именно, обработки исключений. Вот как API должен указывать на сбои.

почему этот шаблон [return a tuple] не является более распространенным? Что с этим не так?

Основная ошибка этого шаблона заключается в том, что он использует механизм сообщения об ошибках способом, чуждым Java. Если в вашем API произошел сбой, создайте исключение. Это избавляет вас от создания вдвое большего количества объектов, чем необходимо в «основных» случаях, и делает ваши API интуитивно понятными для людей, которые хорошо изучили библиотеку классов Java.

Бывают ситуации, когда возврат a null может быть интерпретирован обоими способами — как сбой и как законное возвращаемое значение. Хорошим примером может служить поиск объектов в ассоциативных контейнерах: когда вы предоставляете ключ, которого нет в map, можно заявить, что это ошибка программирования, и выдать исключение (это делает библиотека классов .NET) или утверждать, что когда ключ отсутствует, соответствующее место на карте содержит значение по умолчанию, т. Е. null — так это делается в Java. В подобных ситуациях вполне допустимо возвращать кортеж. В Java Map решили отказаться от этого, скорее всего, чтобы сэкономить на создании дополнительных объектов каждый раз, когда объект запрашивается у Map .

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

1. Вот почему я использовал слово «обычно» и привел пример, где null может быть допустимым возвращаемым значением :-). Вы немного правы в том, что «делает ваши API интуитивно понятными для людей, которые хорошо изучили библиотеку классов Java».