Должен ли метод извлечения возвращать "null" или вызывать исключение, когда он не может создать возвращаемое значение? [закрытый]

У меня есть метод, который должен возвращать объект, если он найден.

Если он не найден, должен ли я:

  1. вернуть null
  2. исключение
  3. другое

30 ответов


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

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

более важно: что вы делаете в других местах кода? Последовательность важна.


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

в противном случае это вопрос предпочтений.


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

Что бы вы ни делали, я настоятельно рекомендую третий вариант: возврат строки с надписью "WTF".


Если null никогда не указывает на ошибку, просто верните null.

Если null всегда является ошибкой, то создайте исключение.

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

трудно злоупотреблять рутиной Try. Очень легко забыть проверить. ноль.

поэтому, когда null является ошибкой, вы просто пишете

object o = FindObject();

когда null не является ошибкой, вы можете закодировать что-то вроде

if (TryFindObject(out object o)
  // Do something with o
else
  // o was not found

Я просто хотел повторить варианты, упомянутые ранее, бросая некоторые новые:

  1. вернуть null
  2. исключение
  3. использовать шаблон null объект
  4. предоставьте метод boolean parameter, чтобы вызывающий мог выбрать, хочет ли он, чтобы вы создали исключение
  5. предоставьте дополнительный параметр, чтобы вызывающий абонент мог установить значение, которое он возвращает, если значение не найдено

или вы можете объединить эти параметры:

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

Object findObjectOrNull(String key);
Object findObjectOrThrow(String key) throws SomeException;
Object findObjectOrCreate(String key, SomeClass dataNeededToCreateNewObject);
Object findObjectOrDefault(String key, Object defaultReturnValue);

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

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


использовать шаблон null object или бросить исключение.


будьте в соответствии с API(ы), которые вы используете.


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

краткая версия: используйте исключения для обработки исключительного поведения, а не для обработки нормального потока управления в вашей программе.

-Алан.


Это зависит от того, способствует ли ваш язык и код: LBYL (семь раз отмерь) или EAFP (проще попросить прощения, чем разрешения)

LBYL говорит, что вы должны проверить значения (поэтому верните null)
EAFP говорит, чтобы просто попробовать операцию и посмотреть, если это не удастся (бросьте исключение)

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


EAFP против LBYL в Python:
http://mail.python.org/pipermail/python-list/2003-May/205182.html (Веб-Архив)


преимущества исключение:

  1. более чистый поток управления в вашем вызывающем коде. проверка на null вводит условную ветвь, которая изначально обрабатывается try / catch. Проверка на null не указывает на то, что вы проверяете - Вы проверяете на null, потому что ищете ошибку, которую ожидаете, или вы проверяете на null, чтобы не передавать ее дальше по цепочке?
  2. устраняет двусмысленность того, что "нуль" означает. является ли null представителем ошибки или null, что на самом деле хранится в значении? Трудно сказать, когда у вас есть только одна вещь, чтобы основывать эту решимость.
  3. улучшена согласованность между поведением метода в приложении. исключения обычно отображаются в сигнатурах методов, поэтому вы можете лучше понять, для каких крайних случаев методы в учетной записи приложения и на какую информацию ваше приложение может реагировать в предсказуемом манера.

для получения дополнительных объяснений с примерами см.:http://metatations.com/2011/11/17/returning-null-vs-throwing-an-exception/


исключения связаны с проектированием по контракту.

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

1) все входные данные метод действителен, в этом случае вы должны вернуть null, когда объект не найден.

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

is_present(key)
find(key) throws Exception

если и только если вы предоставляете оба метода 2-го контракта, вам разрешено бросить исключение, ничего не найдено!


Я предпочитаю просто возвращать значение null, и полагаться на абонента, чтобы обработать его соответствующим образом. Исключение (за неимением лучшего слова) - если я абсолютно "уверен", что этот метод вернет объект. В таком случае неудача-это исключительный должен и должен бросать.


зависит от того, что это означает, что объект не найден.

Если это нормальное состояние дел, то вернет null. Это просто то, что может произойти время от времени, и звонящие должны проверить это.

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

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


вот еще несколько предложений.

при возврате коллекции избегайте возврата null, возвращайте пустую коллекцию,что упрощает обработку перечисления без проверки null.

несколько .NET API используют шаблон параметра thrownOnError, который дает вызывающему объекту выбор, является ли это действительно исключительной ситуацией или нет, если объект не найден. Тип.Примером этого является GetType. Другой распространенный шаблон с BCL-TryGet шаблон, в котором возвращается логическое значение и значение передается через выходной параметр.

вы также можете рассмотреть шаблон нулевого объекта в некоторых обстоятельствах, который может быть либо по умолчанию, либо версией без поведения. Ключ-избегать нулевых проверок по всей базе кода. См. здесь для получения дополнительной информации http://geekswithblogs.net/dsellers/archive/2006/09/08/90656.aspx


в некоторых функциях я добавляю параметр:

..., bool verify = true)

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


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

В C++ я могу придумать 3 разных варианта настройки метода, который находит объект.

Вариант A

Object *findObject(Key &key);

возвращает null, когда объект не могу найти. Просто и мило. Я бы пошел с этим. Альтернативные подходы ниже предназначены для людей, которые не ненавидят out-params.

Вариант B

void findObject(Key &key, Object &found);

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

Вариант C

bool findObject(Key &key, Object &found);

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

if (!findObject(myKey, myObj)) { ...

ссылаясь только на случай, когда null не считается исключительным поведением, я определенно за метод try, понятно, нет необходимости "читать книгу" или "смотреть, прежде чем прыгать", как было сказано здесь

Так в основном:

bool TryFindObject(RequestParam request, out ResponseParam response)

и это означает, что код пользователя также будет четким

...
if(TryFindObject(request, out response)
{
  handleSuccess(response)
}
else
{
  handleFailure()
}
...

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


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


или вернуть параметр

опция-это в основном класс контейнера, который заставляет клиента обрабатывать случаи стенда. У Scala есть эта концепция, посмотрите, это API.

тогда у вас есть такие методы, как T getOrElse(t valueIfNull) на этом объекте, который либо возвращает найденный объект, либо allternative клиентские спецификации.


пока он должен вернуть ссылка для объекта возврат NULL должен быть хорошим.

однако, если он возвращает всю чертову вещь (как в C++, если вы делаете: 'return blah;' вместо 'return &blah;' (или 'blah' - указатель), то вы не можете вернуть NULL, потому что он не имеет типа 'object'. В этом случае создание исключения или возврат пустого объекта, у которого нет набора флагов успеха, - это то, как я бы подошел к проблеме.


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


Я согласен с тем, что кажется консенсусом здесь (вернуть null, если "не найден" является нормальным возможным результатом, или выбросить исключение, если семантика ситуации требует, чтобы объект всегда был найден).

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


возвращает null, исключения именно таковы: что-то ваш код делает, что не ожидается.


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


предпочитают возвращать null --

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

Если вызывающий абонент действительно не использует его, не облагайте его налогом try/catch блок


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


Если метод возвращает коллекцию, затем возвращает пустую коллекцию (как указано выше). Но, пожалуйста, не коллекции.EMPTY_LIST или такой! (в случае Java)

Если метод восстанавливает один объект, то у вас есть несколько вариантов.

  1. если метод всегда должен находить результат, и это реальный случай исключения, чтобы не найти объект, то вы должны бросить исключение (в Java: пожалуйста, непроверенное исключение)
  2. (только Java), если вы можете терпеть что метод создает проверенное исключение, бросает проект конкретного ObjectNotFoundException или тому подобное. В этом случае компилятор говорит вам, если вы забыли обработать исключение. (Это моя предпочтительная обработка не найденных вещей в Java.)
  3. если вы говорите, что это действительно нормально, если объект не найден, и Ваше имя метода похоже на findBookForAuthorOrReturnNull(..), то вы можете вернуть null. В этом случае это сильно recomminded использовать какой-то статической проверки или проверка компилятора, которая предотвращает разыменование результата без проверки null. В случае Java это может быть например. FindBugs (см. DefaultAnnotation в http://findbugs.sourceforge.net/manual/annotations.html) или IntelliJ-проверка.

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


Если вы используете библиотеку или другой класс, который бросает исключение, вы должны rethrow его. Вот пример. Пример 2.Java-это как библиотека и пример.java использует его объект. Главный.java является примером для обработки этого исключения. Вы должны показать значимое сообщение и (при необходимости) трассировку стека пользователю в вызове сторона.

Main.java

public class Main {
public static void main(String[] args) {
    Example example = new Example();

    try {
        Example2 obj = example.doExample();

        if(obj == null){
            System.out.println("Hey object is null!");
        }
    } catch (Exception e) {
        System.out.println("Congratulations, you caught the exception!");
        System.out.println("Here is stack trace:");
        e.printStackTrace();
    }
}
}

пример.java

/**
 * Example.java
 * @author Seval
 * @date 10/22/2014
 */
public class Example {
    /**
     * Returns Example2 object
     * If there is no Example2 object, throws exception
     * 
     * @return obj Example2
     * @throws Exception
     */
    public Example2 doExample() throws Exception {
        try {
            // Get the object
            Example2 obj = new Example2();

            return obj;

        } catch (Exception e) {
            // Log the exception and rethrow
            // Log.logException(e);
            throw e;
        }

    }
}

Пример 2.java

 /**
 * Example2.java
 * @author Seval
 *
 */
public class Example2 {
    /**
     * Constructor of Example2
     * @throws Exception
     */
    public Example2() throws Exception{
        throw new Exception("Please set the \"obj\"");
    }

}

Это действительно зависит от того, ожидаете ли вы найти объект или нет. Если вы следуете школе мысли, что исключения должны использоваться для указания на что-то, ну, err, исключительное произошло тогда:

  • объект найден; вернуть объект
  • объект не найден; исключение throw

в противном случае возвращает null.