#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».