Альтернативы возвращению NULL - PullRequest
25 голосов
/ 12 мая 2011
   /**
     * Returns the foo with the matching id in this list
     * 
     * @param id the id of the foo to return
     * @return the foo with the matching id in this list
     */
    public Foo getFoo(int id)
    {
        for (Foo foo : list)
        {
            if (foo.getID() == id)
            {
                return foo;
            }
        }

        return null;
    }

Вместо возврата null, когда foo не найден, я должен throw exception?Имеет ли это значение, и есть ли идиома «лучших практик» по этому вопросу?Кстати, я знаю, что мой пример немного надуман, но я надеюсь, что вы поняли идею ...

Спасибо.

РЕДАКТИРОВАТЬ

Изменен код для получения Foo на основе id , чтобы лучше проиллюстрировать реальный сценарий.

Ответы [ 15 ]

20 голосов
/ 12 мая 2011

Возвращение null не только более прост в обращении, но и работает лучше.Исключения должны использоваться для обработки исключительных дел.

11 голосов
/ 12 мая 2011

Я бы сказал, что это зависит от семантики вашего метода.

Будет ли foo почти всегда найден в списке?(например, если это кеш, который содержит конечное количество объектов).Если это так, то отсутствие данных может означать, что что-то пошло не так - например, произошла ошибка при инициализации некоторого приложения или недействителен ключ - и исключение может быть оправдано.

Однако в большинстве случаев явернул бы null.Возможно, клиент знает, что объект может отсутствовать, и имеет закодированную логику для обработки этого случая;если вы используете исключение, этот код будет намного сложнее читать, понимать и поддерживать.

Некоторые API фактически предоставляют два метода: find метод, который может возвращать ноль, и get или load метод, который выдает исключение.

Хммм ... в случае сомнений, ошибка в пользу нуля:)

5 голосов
/ 12 мая 2011

Возвращение null нормально, если оно задокументировано как действительный результат.

Другой вариант - шаблон нулевого объекта.Это - экземпляр Foo, который не имеет никаких данных:

public class Foo {
    public static final Foo NULL_FOO = new Foo();
}

и возвращает его вместо.

4 голосов
/ 12 мая 2011

Лучше избегать исключений, если вы можете, но иногда вы просто не можете.В таком случае, что если вы сохранили null в списке?Вы не можете определить разницу между «найден ноль» и «не удалось найти то, что вы хотели».

Существует шаблон, он называется Типы опций , и он часто используется в Scala,Вы либо возвращаете контейнер с элементом, либо контейнер класса с надписью «Я пустой».Статья в вики даст лучшую картину.

У вас также может быть "есть ли это в коллекции?"метод, который возвращает bool, затем выдает исключение из приведенного выше кода, если вы не проверили сначала.


И просто комментарий, совершенно не связанный с вашим реальным вопросом.Если вы реализуете equals, он должен возвращать true, только если два объекта фактически равны.Поэтому приведенный выше код должен всегда возвращать объект, который вы передаете в него!

4 голосов
/ 12 мая 2011

Я предпочитаю возвращаться null. Это отличный результат для возврата из метода, и ваш код, который вызывает метод, должен обрабатывать нулевые значения соответствующим образом. Это может означать создание исключения в вашем вызывающем коде, но я бы не стал делать это в этом методе.

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

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

3 голосов
/ 12 мая 2011

Лучшей практикой было бы сказать в Javadoc, что null возвращается, когда совпадений не найдено.

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

Другой подход может состоять в том, чтобы вернуть NULL_FOO типа Foo.

Я бы предпочел просто вернуть ноль.

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

1 голос
/ 04 августа 2017

Это старый вопрос, но я не нашел упомянутый здесь необязательный класс гуавы, а также необязательный класс * JDK (из Java 8), который служит той же цели и обладает большей функциональностью.

Эта статья является хорошим обзором причин использования опции guava.Я настоятельно рекомендую прочитать его.

Вот выдержка из текста:

Какой смысл?

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

Это особенно актуально, когда вы возвращаете значения, которые могут или не могут быть"подарок."Вы (и другие) с гораздо большей вероятностью забудете, что other.method (a, b) может вернуть нулевое значение, чем вы, вероятно, забудете, что a может быть нулевым, когда вы реализуете other.method.Возвращение Optional делает невозможным для вызывающих абонентов забыть об этом случае, так как им приходится самим разворачивать объект для компиляции своего кода.

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

public Point2D intersect(Line line)

Или в методе, который ищет значение на карте по ключу.

Если вызвать такой метод и выполнить нулевую проверку результата, это довольноочевидно, что происходит (проверка на наличие параллельных линий или ключ не найден).Я бы предпочел вернуть null, чтобы я не раздул свой код.

Хотя я бы всегда документировал это в javadoc.

1 голос
/ 12 мая 2011

Это может не отвечать на ваш вопрос напрямую, но оно исходит из нескольких замечаний, которые я получил от участников Stackoverflow по схожим темам.

Вместо того, чтобы возвращать null, если foo не найден, я должен вызвать исключение? Имеет ли это значение, и есть ли идиома «лучших практик» по этому вопросу? Кстати, я знаю, что мой пример немного надуман, но я надеюсь, что вы поняли идею ... "

Из того, что я понял, исключения должны быть выброшены из метода, когда исключение касается параметра, данного методу. Например, метод, принимающий экземпляры File, будет генерировать исключение NullPointerException или IOException. Это следует из идеи, что между вызывающим абонентом и вызываемым абонентом существует договор о том, что вызывающий должен отправить действительные объекты и позаботиться о них, если они недействительны.

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

Чтобы завершить, вполне допустимо вернуть нулевой объект, если целью является получение одного экземпляра. Это перефразирует, что объект не был найден или не существует. Когда дело доходит до групп объектов, я думаю, что соглашение состоит в том, чтобы просто возвращать пустую коллекцию / список.

1 голос
/ 12 мая 2011

Я думаю, что это дело вкуса, и в этом случае это также зависит от того, может ли список содержать нулевые значения.Если список может содержать значения NULL, NULL также будет допустимым аргументом метода, и вам нужно будет различать, возвращать ли NULL (например, NULL был передан, найден и возвращен) или сообщать вызывающему методу, что переданное значение NULL было не найдено.

1 голос
/ 12 мая 2011

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

В качестве примечания, вместо того, чтобы вызывать метод-образец для получения, было бы более целесообразно назвать его как-то как Foo findFoo(Foo f), поскольку вы ищете, а не просто получаете.

...