2008-10-06 4 views
412

У меня есть метод, который должен возвращать объект, если он найден.Должен ли метод поиска возвращать 'null' или выдавать исключение, если он не может получить возвращаемое значение?

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

  1. возвращение нулевой
  2. сгенерирует исключение
  3. других
+47

Что бы вы ни делали, убедитесь, что вы задокументировать. Я думаю, что этот момент более важен, чем именно тот подход «наилучший». – Rik 2008-10-06 23:04:48

+6

Это зависит от преобладающих идиом языка программирования. Пожалуйста, пометьте этот вопрос тегом языка программирования. – Teddy 2009-10-01 22:41:14

+2

Возвращение null может означать только успех или неудачу, что очень часто не является большой частью информации (некоторые методы могут потерпеть неудачу разными способами). Библиотекам следует лучше исключать исключения, чтобы сделать ошибки явными, и таким образом основная программа может решить, как обрабатывать ошибку на более высоком уровне (в отличие от встроенной логики обработки ошибок). – 2013-08-20 11:33:13

ответ

409

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

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

Что еще важнее: чем вы занимаетесь в других местах? Согласованность важна.

4

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

13

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

17

Используйте шаблон нулевого объекта или создайте исключение.

+0

Это реальный ответ. Возвращение null - ужасная привычка ленивых программистов. – jeremyjjbrown 2013-05-23 19:01:28

+0

Я не могу поверить, что этот ответ еще не проголосовали. Это реальный ответ, и любой из этих подходов мертв - легко и экономит много раздутий кода или NPE. – Bane 2013-09-06 13:16:57

+0

http://sourcemaking.com/design_patterns/null_object – Bane 2013-09-06 13:35:43

2

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

2

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

0

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

  • Объект найден; возвращенный объект
  • Объект не найден; бросить исключение

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

88

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

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

+0

Я не согласен. Вы можете делать исключения как коды состояния: «NotFoundException» – ACV 2017-10-16 13:37:13

55

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

Независимо от того, что вы делаете, я настоятельно рекомендую использовать третий вариант: Возврат строки с надписью «WTF».

4

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

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

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

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

0

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

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

1

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

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

0

Если не найти это, исключительное событие (т. е. оно должно быть там при нормальных обстоятельствах), а затем бросать. В противном случае верните значение «не найденное» (может быть нулевым, но не обязательным) или даже вернуть метод boolean для найденного/notfound и параметр out для фактического объекта.

0

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

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

0

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

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

3

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

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

Вариант А

Object *findObject(Key &key); 

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

Вариант Б

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

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

Вариант C

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

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

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

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

1

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

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

0

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


DataType GetObject(DBConnection conn, string id, bool throwOnNotFound) { 
    DataType retval = ... // find object in database 
    if (retval != null || ! throwOnNotFound) { 
     return retval; 
    } else { 
     throw new NoRowsFoundException("DataType object with id {id} not found in database"); 
    } 
} 

DataType GetObject(DBConnection conn, string id) { 
    return GetObject(conn, id, true); 
} 
45

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

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

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

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

Таким образом, когда пустой ошибка просто написать

object o = FindObject(); 

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

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

Или возвращать вариант

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

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

1

Вернуть нуль, исключения в точности таковы: что-то не делает ваш код.

10

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

LBYL говорит, что вы должны проверить для значений (так возвращает нуль)
ЭСПЦ говорит просто попробовать операцию и посмотреть, если он выходит из строя (сгенерировано исключение)

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


ЭСПЦ против LBYL в Python:
http://mail.python.org/pipermail/python-list/2003-May/205182.html (Web Archive)

3

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

..., bool verify = true) 

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

0

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

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

Если объект не содержит объект, это указывает на нечастое событие. (Как и кто-то удалил элемент из магазина, пока вы проверяли элемент одновременно.) Затем выполните исключение.

12

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

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

-Alan.

1

Исключения составляют исключительные. Верните нуль , если он верен null.

5

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

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

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

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

is_present(key) 
find(key) throws Exception 

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

4

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

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

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

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

22

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

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

Или вы можете комбинировать эти варианты:

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

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

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

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

3

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

сказали

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

bool TryFindObject(RequestParam request, out ResponseParam response) 

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

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

предпочитает возвращающийся нуль -

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

Если абонент не реально использовать его, не облагает его try/catch блок

1

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

4

Преимущества бросать исключение:

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

Для более подробного объяснения с примерами, см: http://metatations.com/2011/11/17/returning-null-vs-throwing-an-exception/

1

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

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

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

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

1

Если вы используете библиотеку или другой класс, создающий исключение, вы должны rethrow. Вот пример. Example2.java похож на библиотеку, а Example.java использует его. Main.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(); 
    } 
} 
} 

Example.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; 
     } 

    } 
} 

Example2.java

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

}