Подтвердить что ты не робот

Альтернативы возврату NULL

   /**
     * 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 a exception? Это имеет значение, и есть ли "передовая практика" идиома по этому вопросу? Кстати, я знаю, что мой пример немного ухищрен, но я надеюсь, что вы поняли...

Спасибо.

ИЗМЕНИТЬ

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

и верните его.

Ответ 4

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

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

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

Ответ 5

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

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

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


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

Ответ 6

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

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

Другой подход может заключаться в возврате значения NULL_FOO типа Foo.

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

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

Ответ 7

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

Ответ 8

Возвращение null вполне приемлемо. Я бы добавил лишнюю пару дюжин нажатий клавиш и документировал возможность возврата null в JavaDoc.

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

Ответ 9

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

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

Ответ 10

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

Ответ 11

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

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

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

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

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

Ответ 12

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

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

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

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

Ответ 13

Другая точка зрения, которую я еще не прочитал в других ответах, такова:

В list содержится null от Foo? Если это так, не было бы известно, был ли найден идентификатор или связан ли он с null. В этом случае:

  • Выбросить исключение, если список не найден;
  • Оберните результат другим объектом (например, a SearchResult, в котором указывается, был ли найден объект, его ассоциативный идентификатор, результат или нет результата, если он не был найден);
  • Создайте метод existsFoo(int id).

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

  • Как отмечали другие, Exception являются исключительными;
  • Оболочка SearchResult должна выделяться каждый раз, когда вы хотите повторить поиск. Обертка может быть изменена, но это создает множество новых трудностей и проблем;
  • existsFoo должен искать список, который удваивает стоимость знания того, существует или нет ключ.

Вообще я могу сказать:

  • Является ли идентификатор не признанным исключительным? Используйте IllegalArgumentException;
  • Является ли результат передан другим классам, используемым в качестве объекта? Используйте обертку, например. SearchResult;
  • Является ли с getFoo проверено только оно null или нет (существует или нет)? Используйте другой метод existsFoo.

Ответ 14

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

Аннотация выдержка ниже поможет вам не попасть в старые способы...

Использовать @Nullable

Чтобы устранить NullPointerExceptions в вашей кодовой базе, вы должны быть дисциплинированный о нулевых ссылках. Мы добились успеха в этом следуя и применяя простое правило:

Каждый параметр не равен null, если явно не указано.

Гуава: основные библиотеки Google для Java и JSR-305 имеют простые API для получения нулей под контролем. Preconditions.checkNotNull можно использовать для быстрого выхода из строя, если найдена нулевая ссылка, и @Nullable может использоваться для аннотирования параметра, который допускает нулевое значение:

import static com.google.common.base.Preconditions.checkNotNull;
import static javax.annotation.Nullable;

public class Person {
  ...

  public Person(String firstName, String lastName, @Nullable Phone phone) {
    this.firstName = checkNotNull(firstName, "firstName");
    this.lastName = checkNotNull(lastName, "lastName");
    this.phone = phone;
  }

Если null допустимо вашим классом, вы можете аннотировать поле или с помощью @Nullable... с помощью любой @Nullable аннотации, например edu.umd.cs.findbugs.annotations.Nullable или javax.annotation.Nullable.

Ответ 15

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

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

Здесь выдержка:

Какая точка?

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

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

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

public Point2D intersect(Line line)

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

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

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