java api design - NULL или исключение

17

Лучше ли вернуть значение null или исключить исключение из метода API?

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

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

Любые звуковые и практические советы?

    
задан srini.venigalla 11.02.2010 в 17:27
источник
  • Знаете ли вы о шаблоне Null Object? en.wikipedia.org/wiki/Null_Object_pattern –  Thorbjørn Ravn Andersen 14.06.2010 в 08:31

10 ответов

5

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

Например, если вы пишете XML-парсер, вы можете реализовать такие методы, как:

/**
 * Returns first child element with matching element name or else
 * throws an exception.  Will never return null.
 */
Element getMandatoryChildElement(Element parent, String elementName)
throws MissingElementException;

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

    
ответ дан Adamski 11.02.2010 в 17:29
  • @Adamski - Мне это нравится. Но если вызывающий абонент уже выбрасывает исключение, это исключение будет отключено правильно? Напр. Я видел, что NumberFormatException не обрабатывается, потому что вызывающий абонент уже выбрасывает исключение. Итак, есть ли правило следствия - метод никогда не должен бросать «Исключение» сам? –  srini.venigalla 11.02.2010 в 17:48
  • Я придерживаюсь мнения, что если элемент не может быть найден, вызывающий не должен был вызывать метод с самого начала. У вас может быть метод проверки, поэтому вы можете написать if (hasElement ('test')) getMandatoryChildElement ('test') ;. Гораздо лучше, чем попытка try-catch-try следующего метода сортировать код. Как бы вы написали метод «load file»? –  Alexander Torstling 11.02.2010 в 18:19
  • @disown: проблема заключается в предоставлении метода в API. Вы не знаете пользователя, поэтому вам нужно убедиться, что он не будет делать плохие вещи и сказать ему, когда он делает неправильно. @ srini.venigalla: если вызывающий абонент уже выбрасывает Exception, он рано или поздно поймает его. –  Valentin Rocher 11.02.2010 в 18:22
  • @disown: Еще один аспект этого подхода заключается в том, что чем меньше ветвей в вашем коде (то есть, если есть), тем легче тестировать. Проверьте мой ответ здесь для получения дополнительной информации: stackoverflow.com/questions/1274792/.... –  Adamski 11.02.2010 в 18:30
  • @Adamski, Valentin: Я согласен с тем, что должна быть ошибка, я просто утверждал, что исключение MissingElementException должно быть исключением во время выполнения. Если вы включите проверку ошибок в нормальный поток, вы все равно получите еще одну ветку. В любом случае вам обычно нужно разбираться с исключительным случаем, вопрос в том, как вы к нему подходите: реактивно или проактивно. Я предпочитаю проактивно. –  Alexander Torstling 11.02.2010 в 18:44
6

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

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

    
ответ дан danben 11.02.2010 в 17:29
  • @danben - Мне очень не нравится API, который генерирует полдюжины пользовательских исключений, и мне нужно добавить стек ненужных обработчиков исключений. Это само по себе не проблема, но если API добавляет новое исключение, вся цепочка восходящего потока должна быть обновлена. Я могу понять все, что необходимо или даже хорошо, но я искал четкие соглашения. –  srini.venigalla 11.02.2010 в 17:45
  • @ srini.venigalla - полдюжины, как правило, слишком много. Имейте в виду, что нет никаких причин, чтобы обязательно иметь ЛЮБЫЕ пользовательские исключения - вы можете выбросить IllegalArgumentException или что-то еще, что соответствует вашим потребностям. –  danben 11.02.2010 в 17:46
  • В чем преимущество наличия специального проверенного исключения, такого как AxisException или SoapException или WhateverException? Все, что вы можете сделать из этого, состоит в том, что исключение исходило из lib, который вы все равно знали из stacktrace. Если вы хотите сделать обработку ошибок с помощью catch-all, ее легко добавить в клиенте. –  Alexander Torstling 11.02.2010 в 18:16
  • @disown: никто не оспаривает этого ... Нет смысла. Также некоторые будут оспаривать саму концепцию проверенных исключений вообще, и есть, безусловно, очень успешные языки, которые прекрасно справляются, и это будет продолжаться отлично без них. Некоторые даже рассматривают проверенные исключения как прославленные заявления GOTO, так что это наверняка спорная тема. –  SyntaxT3rr0r 11.02.2010 в 18:28
  • Я бы также добавил, что если API позволяет мне установить значение null, тогда он должен чертовски быть нулевым, когда я его получу. Выброс исключения в этом сценарии был бы очень непоследовательным. –  Robin 11.02.2010 в 18:38
Показать остальные комментарии
6

Джош Блох рекомендует возвращать пустые объекты или «единичные» объекты вместо нулевых значений. Ознакомьтесь с Эффективной Java за массу полезных кусочков Java.

    
ответ дан kgrad 11.02.2010 в 17:46
4

Я хочу сказать, что

  

Никогда не возвращать NULL при возврате   type - это коллекция или массив. Если нет возвращаемого значения, верните пустую коллекцию или пустой массив.

Это устраняет необходимость нулевой проверки.

Хорошая практика:

public List<String> getStudentList()
{
     int count = getStudetCount();
     List<String> studentList = new ArrayList<String>(count);
     //Populate studentList logic
     return studentList;
}

Плохая практика: . Никогда не пишите код, как показано ниже.

public List<String> getStudentList()
{
     int count = getStudetCount();
     if(count == 0)
     {
         return null;
     }
     //Populate studentList logic
     return studentList;
}
    
ответ дан Sreejesh 12.02.2010 в 02:29
  • Если предполагается, что ученик должен быть неизменным: вам будет лучше проверять счет, возвращаемый getStudentCount (), а затем использовать Collections.emptyList (), если оно равно 0. Это позволяет потенциальную оптимизацию (т. е. иметь кешированную пустую список, который возвращается каждый раз). Если счетчик не был 0, вы все равно должны обернуть возвращенный список в обертке Collections.unmodifiableList (studentList), чтобы убедиться, что он не был случайно изменен. –  cdmckay 12.02.2010 в 21:17
3

В Практический дизайн API , автор советует не возвращать значение null ( см. стр.24 ).

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

Я также настоятельно рекомендую прочитать « Нулевые ссылки: ошибка в миллиард долларов "авторитетом в этой области (Т. Хоар).     

ответ дан ewernli 11.02.2010 в 17:50
  • +1 к вам - если только кто-то может поместить значение вокруг времени, затраченного на устранение нулевых проблем и проблем с memcpy. –  srini.venigalla 11.02.2010 в 17:56
  • * млрд. долл. США: P –  Jeriko 13.06.2010 в 13:55
  • @ Джерико, ты прав. исправлено. –  ewernli 14.06.2010 в 08:16
2

Я склонен возвращать null методам getXXX, которые, например, извлекают что-то из базы данных example: User getUserByName (String name) в порядке для возврата null

Но, если вам НЕОБХОДИМО, что вы выберете, вызвав метод, выполните исключение: ex: getDatabaseConnection () должен всегда возвращать соединение и никогда не возвращать null

И для методов, возвращающих коллекцию или массив, НИКОГДА не возвращайте значение null. верните пустую коллекцию / массив вместо

    
ответ дан chburd 11.02.2010 в 17:39
  • Обычно я использую findXXX для методов, которые могут возвращать null, и getXXX для тех, кто этого не сделает. –  Alexander Torstling 11.02.2010 в 18:12
  • @disown - это будет нестандартным и запретить использование библиотек java bean и кода. –  Robin 11.02.2010 в 18:32
  • @Robin: Если вы пишете API базы данных? Javabeans предназначены для простых классов типа DTO. Вы не всерьез назовёте все свои методы в соответствии со спецификацией java beans? –  Alexander Torstling 11.02.2010 в 18:37
  • @disown - Конечно нет. Мой плохой, хотя для прыжка вокруг чтения нескольких ответов и комментариев (не обязательно в порядке). Таким образом, я не связывал ваш комментарий с примером в этом ответе. Мои извинения. –  Robin 11.02.2010 в 21:02
1

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

    
ответ дан Poindexter 11.02.2010 в 17:29
1

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

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

    
ответ дан Nils Schmidt 11.02.2010 в 17:41
1

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

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

Например:

ProcessResult performLibraryTask(TaskSpecification ts)

Таким образом, вы можете иметь ProcessResult указать условия ошибки:

ProcessResult result = performLibraryTask(new FindSmurfsTaskSpecification(SmurfColor.BLUE));
if (result.failed()) {
    throw new RuntimeException(result.error());
}

Этот подход похож на обратный нулевой подход, но вы можете отправить дополнительную информацию клиенту.

ИЗМЕНИТЬ:

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

if (currentPeriod().equals(SmurfColor.BLUE) && SmurfColor.GREEN.equals(taskSpecification.getSmurfColor()) {
    throw new IllegalStateException("Cannot search for green smurfs during blue period, invalid request");
}

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

ответ дан Alexander Torstling 11.02.2010 в 17:39
  • Мне это нравится, но такой подход, как нулевой возврат, требует милосердия звонящего. –  srini.venigalla 11.02.2010 в 17:50
  • Согласовано. Вам нужно будет установить предел, для которого ошибки, которые, по вашему мнению, могут произойти при нормальном использовании, и моделировать их как исключения. Ошибки кодирования (нарушение контракта) или вещи, которые вы не видите во время обычного использования (например, вне памяти, разыменование нулевого указателя), вы должны использовать исключения. –  Alexander Torstling 11.02.2010 в 18:04
0

Обычно я рассматриваю аналогичный пример в JDK.

В случае аксессуаров имеет смысл использовать Null.

Нуль следует использовать для указания отсутствующих или неизвестных данных.

Для всего остального он обычно представляет собой ошибку при обработке.

Теперь у нас есть 2 варианта, время выполнения и время компиляции. Вот примеры того, как я ищу аналогию.

Позволяет извлечь пример из ArrayIndexOutOfBoundException. Доступ или изменение элемента - очень распространенная операция.

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

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

    
ответ дан user1317764 15.08.2012 в 17:13