Избегание!= Null
Я использую object != null
чтобы избежать NullPointerException
.
Есть ли хорошая альтернатива этому?
Например:
if (someobject != null) {
someobject.doCalc();
}
Это позволяет избежать NullPointerException
, когда неизвестно, является ли объект null
или нет.
Обратите внимание, что принятый ответ может быть устаревшим, см. qaru.site/info/50/... для более позднего подхода.
Ответы
Ответ 1
Это для меня звучит как довольно распространенная проблема, с которой младшие к средним разработчикам склонны сталкиваться в какой-то момент: они либо не знают, либо не доверяют контрактам, в которых они участвуют, и оборонительно переоценивают для нулей. Кроме того, при написании собственного кода они склонны полагаться на возврат нулей, чтобы указать что-то, что требует от вызывающего пользователя проверки нулей.
Иными словами, есть два случая, когда возникает нулевая проверка:
(2) легко. Используйте либо утверждения assert
(утверждения), либо допускайте сбой (например, NullPointerException). Утверждения - это очень малоиспользуемая функция Java, добавленная в 1.4. Синтаксис:
assert <condition>
или
assert <condition> : <object>
где <condition>
- это логическое выражение, а <object>
- объект, результат вывода метода toString()
будет включен в ошибку.
Инструкция assert
вызывает Error
(AssertionError
), если условие не соответствует действительности. По умолчанию Java игнорирует утверждения. Вы можете включить утверждения, передав опцию -ea
в JVM. Вы можете включать и отключать утверждения для отдельных классов и пакетов. Это означает, что вы можете проверить код с помощью утверждений при разработке и тестировании и отключить их в рабочей среде, хотя мое тестирование показало рядом с отсутствием влияния производительности на утверждения.
Не использовать утверждения в этом случае ОК, потому что код будет просто терпеть неудачу, что и произойдет, если вы используете утверждения. Единственное различие заключается в том, что с утверждениями это может произойти раньше, более значимым образом и, возможно, с дополнительной информацией, которая может помочь вам понять, почему это произошло, если вы этого не ожидали.
(1) немного сложнее. Если у вас нет контроля над кодом, который вы вызываете, вы застряли. Если null является допустимым ответом, вы должны проверить его.
Если это код, который вы контролируете, однако (и это часто бывает), то это другая история. Избегайте использования нулей в качестве ответа. С помощью методов, возвращающих коллекции, легко: возвращать пустые коллекции (или массивы) вместо нулей почти все время.
С не-коллекциями это может быть сложнее. Рассмотрим это как пример: если у вас есть эти интерфейсы:
public interface Action {
void doSomething();
}
public interface Parser {
Action findAction(String userInput);
}
где Parser берет необработанный пользовательский ввод и находит что-то, что нужно сделать, возможно, если вы каким-то образом реализуете интерфейс командной строки. Теперь вы можете сделать контракт, что он возвращает null, если нет соответствующего действия. Это приводит к нулевой проверке, о которой вы говорите.
Альтернативное решение - никогда не возвращать null и вместо этого использовать шаблон Null Object:
public class MyParser implements Parser {
private static Action DO_NOTHING = new Action() {
public void doSomething() { /* do nothing */ }
};
public Action findAction(String userInput) {
// ...
if ( /* we can't find any actions */ ) {
return DO_NOTHING;
}
}
}
Для сравнения:
Parser parser = ParserFactory.getParser();
if (parser == null) {
// now what?
// this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
// do nothing
} else {
action.doSomething();
}
к
ParserFactory.getParser().findAction(someInput).doSomething();
что намного лучше, потому что оно приводит к более сжатому коду.
Тем не менее, возможно, совершенно необходимо, чтобы метод findAction() выдавал исключение со значимым сообщением об ошибке - особенно в этом случае, когда вы полагаетесь на ввод пользователя. Было бы намного лучше, если метод findAction выбрал исключение, чем для вызывающего метода, чтобы взорвать простое исключение NullPointerException без объяснения.
try {
ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
userConsole.err(anfe.getMessage());
}
Или, если вы считаете, что механизм try/catch слишком уродливый, а не Do Nothing, ваше действие по умолчанию должно обеспечивать обратную связь с пользователем.
public Action findAction(final String userInput) {
/* Code to return requested Action if found */
return new Action() {
public void doSomething() {
userConsole.err("Action not found: " + userInput);
}
}
}
Ответ 2
Если вы используете (или планируете использовать) Java IDE, такую как JetBrains IntelliJ IDEA, Eclipse или Netbeans, или инструмент, подобный findbugs, то вы можете использовать аннотации для решения этой проблемы.
По сути, у вас есть @Nullable
и @NotNull
.
Вы можете использовать в методе и параметрах, как это:
@NotNull public static String helloWorld() {
return "Hello World";
}
или же
@Nullable public static String helloWorld() {
return "Hello World";
}
Второй пример не скомпилируется (в IntelliJ IDEA).
Когда вы используете первую функцию helloWorld()
в другом фрагменте кода:
public static void main(String[] args)
{
String result = helloWorld();
if(result != null) {
System.out.println(result);
}
}
Теперь компилятор IntelliJ IDEA скажет вам, что проверка бесполезна, поскольку функция helloWorld()
никогда не вернет значение null
.
Используя параметр
void someMethod(@NotNull someParameter) { }
если вы напишите что-то вроде:
someMethod(null);
Это не скомпилируется.
Последний пример с использованием @Nullable
@Nullable iWantToDestroyEverything() { return null; }
Делая это
iWantToDestroyEverything().something();
И вы можете быть уверены, что этого не произойдет. :)
Это хороший способ позволить компилятору проверить что-то большее, чем обычно, и заставить ваши контракты быть сильнее. К сожалению, это поддерживается не всеми компиляторами.
В IntelliJ IDEA 10.5 и далее они добавили поддержку любых других реализаций @Nullable
@NotNull
.
Смотрите сообщение в блоге. Более гибкие и настраиваемые аннотации @Nullable/@NotNull.
Ответ 3
Если значения null не допускаются
Если ваш метод вызывается извне, начните с чего-то вроде этого:
public void method(Object object) {
if (object == null) {
throw new IllegalArgumentException("...");
}
Затем в остальной части этого метода вы узнаете, что object
не является нулевым.
Если это внутренний метод (не часть API), просто документируйте, что он не может быть нулевым, и что он.
Пример:
public String getFirst3Chars(String text) {
return text.subString(0, 3);
}
Однако, если ваш метод просто передает значение, а следующий метод передает его и т.д., это может стать проблематичным. В этом случае вы можете проверить аргумент, как указано выше.
Если null разрешен
Это действительно зависит. Если вы обнаружите, что я часто делаю что-то вроде этого:
if (object == null) {
// something
} else {
// something else
}
Итак, я развожу и делаю две совершенно разные вещи. Существует не уродливый фрагмент кода, потому что мне действительно нужно делать две разные вещи в зависимости от данных. Например, следует ли мне работать с вводом, или я должен рассчитать хорошее значение по умолчанию?
Мне действительно редко приходится использовать идиому "if (object != null && ...
".
Вам может быть проще привести примеры, если вы покажете примеры того, где вы обычно используете идиому.
Ответ 4
Ничего себе, я почти не хочу добавлять еще один ответ, когда у нас есть 57 разных способов рекомендовать NullObject pattern
, но я думаю, что некоторым людям, заинтересованным в этом вопросе, может понравиться знать, что есть предложение в таблице для Java 7 добавить "нуль-безопасная обработка" — упрощенный синтаксис для логики if-not-equal-null.
Пример, данный Алексом Миллером, выглядит следующим образом:
public String getPostcode(Person person) {
return person?.getAddress()?.getPostcode();
}
?.
означает только отмену ссылки на левый идентификатор, если он не является нулевым, иначе оценивайте остальную часть выражения как null
. Некоторым людям, таким как член группы "Пост" Дик Уолл и избиратели в Devoxx, действительно любят это предложение, но есть и оппозиция на том основании, что он будет на самом деле поощрять большее использование null
в качестве контрольного значения.
Обновление: официальное предложение для нулевого оператора в Java 7 было отправлено в Project Coin. Синтаксис немного отличается от приведенного выше примера, но это же понятие.
Обновление. Предложение с нулевым безопасным оператором не попало в проектную монету. Таким образом, вы не увидите этот синтаксис в Java 7.
Ответ 5
Если неопределенные значения не разрешены:
Вы можете настроить свою среду IDE, чтобы предупредить вас о потенциальной нулевой разыменовании. Например, в Eclipse см. настройки> Java> Компилятор> Ошибки/Предупреждения/Нулевой анализ.
Если разрешены неопределенные значения:
Если вы хотите определить новый API, где неопределенные значения имеют смысл, используйте шаблон Option (может быть знаком с функциональными языками). Он имеет следующие преимущества:
- В API явно указано, существует или нет вход или выход.
- Компилятор заставляет вас обрабатывать "undefined" случай.
- Вариант - монада, поэтому нет необходимости в проверке верных нулей, просто используйте map/foreach/getOrElse или аналогичный комбинатор, чтобы безопасно использовать значение (пример).
Java 8 имеет встроенный Optional
класс (рекомендуется); для более ранних версий существуют альтернативы библиотеки, например, Optional
Guava Option
или FunctionalJava. Но, как и многие функциональные шаблоны, использование Option в Java (даже 8) приводит к довольно некоторому шаблону, который вы можете уменьшить, используя менее подробный язык JVM, например Scala или Xtend.
Если вам нужно иметь дело с API, который может возвращать значения NULL, вы не можете многое сделать на Java. У Xtend и Groovy есть оператор Элвиса ?:
И нульбезопасный оператор разыменования ?.
, но обратите внимание, что это возвращает null в случае нулевой ссылки, поэтому он просто "отменяет" правильную обработку нулевого значения.
Ответ 6
Только для этой ситуации -
Не проверять, имеет ли переменная значение null перед вызовом метода equals (пример сравнения строк ниже):
if ( foo.equals("bar") ) {
// ...
}
приведет к NullPointerException
если foo
не существует.
Вы можете избежать этого, если сравнить свою String
следующим образом:
if ( "bar".equals(foo) ) {
// ...
}
Ответ 7
В Java 8 появился новый класс java.util.Optional
, который, возможно, решает некоторые проблемы. Можно хотя бы сказать, что это улучшает читаемость кода, а в случае публичных API-интерфейсов упрощает работу API-интерфейса с разработчиком-клиентом.
Они работают так:
Необязательный объект для данного типа (Fruit
) создается как возвращаемый тип метода. Он может быть пустым или содержать объект Fruit
:
public static Optional<Fruit> find(String name, List<Fruit> fruits) {
for (Fruit fruit : fruits) {
if (fruit.getName().equals(name)) {
return Optional.of(fruit);
}
}
return Optional.empty();
}
Теперь посмотрите на этот код, где мы ищем список Fruit
(fruits
) для данного экземпляра Fruit:
Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
Fruit fruit = found.get();
String name = fruit.getName();
}
Вы можете использовать оператор map()
для выполнения вычисления - или извлечения значения из - необязательного объекта. orElse()
позволяет вам предоставить резервную копию для отсутствующих значений.
String nameOrNull = find("lemon", fruits)
.map(f -> f.getName())
.orElse("empty-name");
Конечно, проверка нулевого/пустого значения по-прежнему необходима, но по крайней мере разработчик осознает, что значение может быть пустым и риск забыть проверить ограничен.
В API, построенном с нуля, с помощью Optional
, когда возвращаемое значение может быть пустым и возвращает простой объект, только если он не может быть null
(соглашение), клиентский код может отказаться от нулевых проверок на значениях возвращаемых объектов...
Конечно, Optional
также может использоваться как аргумент метода, возможно, лучший способ указать необязательные аргументы, чем 5 или 10 методов перегрузки в некоторых случаях.
Optional
предлагает другие удобные методы, такие как orElse
, которые позволяют использовать значение по умолчанию, и ifPresent
, которое работает с лямбда-выражения.
Я приглашаю вас прочитать эту статью (мой основной источник для написания ответа), в которой проблема NullPointerException
(и вообще нулевой указатель), а также (частичное) решение, принесенное Optional
, хорошо объяснены: Дополнительные объекты Java.
Ответ 8
В зависимости от того, какие объекты вы проверяете, вы можете использовать некоторые из классов в общих долях apache, например: apache commons lang и коллекции коллекций apache
Пример:
String foo;
...
if( StringUtils.isBlank( foo ) ) {
///do something
}
или (в зависимости от того, что вам нужно проверить):
String foo;
...
if( StringUtils.isEmpty( foo ) ) {
///do something
}
Класс StringUtils является лишь одним из многих; есть довольно много хороших классов в достоянии, которые делают нулевую безопасную манипуляцию.
Ниже приведен пример того, как вы можете использовать null vallidation в JAVA, когда вы включаете библиотеку apache (commons-lang-2.4.jar)
public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
return read(new StringReader(xml), true, validationEventHandler);
}
И если вы используете Spring, Spring также имеет ту же функциональность в своем пакете, см. библиотеку (spring -2.4.6.jar)
Пример использования этого статического класса f из spring (org.springframework.util.Assert)
Assert.notNull(validationEventHandler,"ValidationHandler not Injected");
Ответ 9
- Если вы считаете, что объект не должен быть нулевым (или это ошибка), используйте assert.
- Если ваш метод не принимает нулевые параметры, скажите это в javadoc и используйте assert.
Вам нужно проверить объект!= null, только если вы хотите обработать случай, когда объект может быть пустым...
Есть предложение добавить новые аннотации в Java7, чтобы помочь с параметрами null/notnull:
http://tech.puredanger.com/java7/#jsr308
Ответ 10
Я поклонник кода "fail fast". Спросите себя: вы делаете что-то полезное в случае, когда параметр равен нулю? Если у вас нет четкого ответа на то, что должен делать ваш код в этом случае... I.e. он никогда не должен быть нулевым, а затем игнорировать его и позволять бросать NullPointerException. Вызывающий код будет иметь такое же значение NPE, как и исключение IllegalArgumentException, но разработчику будет легче отлаживать и понимать, что пошло не так, если NPE выбрасывается, а не ваш код, пытающийся выполнить некоторые другие непредвиденные непредвиденные обстоятельства логики, что в конечном итоге приводит к тому, что приложение все равно не работает.
Ответ 11
Структура коллекций Google предлагает хороший и элегантный способ достижения нулевой проверки.
В библиотечном классе есть метод:
static <T> T checkNotNull(T e) {
if (e == null) {
throw new NullPointerException();
}
return e;
}
И используется (с import static
):
...
void foo(int a, Person p) {
if (checkNotNull(p).getAge() > a) {
...
}
else {
...
}
}
...
Или в вашем примере:
checkNotNull(someobject).doCalc();
Ответ 12
Иногда у вас есть методы, которые работают с его параметрами, которые определяют симметричную операцию:
a.f(b); <-> b.f(a);
Если вы знаете, что b никогда не может быть нулевым, вы можете просто поменять его. Он наиболее полезен для равных:
Вместо foo.equals("bar");
лучше сделать "bar".equals(foo);
.
Ответ 13
Вместо шаблона нулевого объекта, который имеет свои применения, вы можете рассмотреть ситуации, когда нулевой объект является ошибкой.
Когда генерируется исключение, просмотрите трассировку стека и выполните ошибку.
Ответ 14
Нуль - это не проблема. Он является неотъемлемой частью полного набора инструментов моделирования. Программное обеспечение нацелено на моделирование сложности мира, и нуль несет его бремя. Null указывает "Нет данных" или "Неизвестно" на Java и тому подобное. Поэтому для этих целей целесообразно использовать nulls. Я не предпочитаю шаблон "Нулевой объект"; Я думаю, что он поднимает " кто будет охранять проблему опекунов ".
Если вы спросите меня, как зовут мою подругу, я скажу вам, что у меня нет подруги. На языке Java я верну null. Альтернативой было бы вывести осмысленное исключение, чтобы указать на какую-то проблему, которая не может быть (или не хочет быть) решена прямо там и делегировать ее где-то выше в стеке, чтобы повторить попытку или сообщить об ошибке доступа к данным пользователю.
-
Для "неизвестного вопроса" дайте "неизвестный ответ". (Небезопасно, если это правильно с бизнес-точки зрения). Проверка аргументов для null один раз внутри метода до использования освобождает нескольких вызывающих лиц от проверки их перед вызовом.
public Photo getPhotoOfThePerson(Person person) {
if (person == null)
return null;
// Grabbing some resources or intensive calculation
// using person object anyhow.
}
Предыдущий приводит к нормальному логическому потоку, чтобы получить фотографию несуществующей подруги из моей фотобиблиотеки.
getPhotoOfThePerson(me.getGirlfriend())
И это соответствует новому API Java (с нетерпением ждущего)
getPhotoByName(me.getGirlfriend()?.getName())
Хотя довольно "нормальный бизнес-поток" не позволяет найти фотографию, хранящуюся в БД для какого-то человека, я использовал пару, как показано ниже, для некоторых других случаев
public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
public static MyEnum parseMyEnumOrNull(String value);
И не следует ненавидеть вводить <alt> + <shift> + <j>
(генерировать javadoc в Eclipse) и писать три дополнительных слова для публичного API. Этого будет более чем достаточно для всех, кроме тех, кто не читает документацию.
/**
* @return photo or null
*/
или
/**
* @return photo, never null
*/
-
Это довольно теоретический случай, и в большинстве случаев вам следует предпочесть java null safe API (если он будет выпущен еще через 10 лет), но NullPointerException
является подклассом Exception
. Таким образом, это форма Throwable
которая указывает условия, которые разумное приложение может захотеть поймать (javadoc)! Чтобы использовать первое преимущество привилегий и отдельный код обработки ошибок из "обычного" кода (по словам создателей Java), уместно, как и я, поймать NullPointerException
.
public Photo getGirlfriendPhoto() {
try {
return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
} catch (NullPointerException e) {
return null;
}
}
Могут возникнуть вопросы:
В. Что если getPhotoDataSource()
возвращает значение null?
О. Это деловая логика. Если мне не удастся найти фотоальбом, я покажу вам никаких фотографий. Что делать, если appContext не инициализирован? Этот метод бизнес-логики справляется с этим. Если одна и та же логика должна быть более строгой, то бросание исключения является частью бизнес-логики, а явная проверка на нуль должна использоваться (случай 3). Новый Java Null-safe API лучше подходит для того, чтобы указать выборочно, что подразумевает, и что не означает, что его инициализация будет неудачной в случае ошибок программиста.
Q. Резервный код может быть выполнен и ненужные ресурсы могут быть захвачены.
A. Это может произойти, если getPhotoByName()
попытается открыть соединение с базой данных, создать PreparedStatement
и, наконец, использовать имя человека в качестве параметра SQL. Подход к неизвестному вопросу дает неизвестный ответ (случай 1). Перед захватом ресурсов метод должен проверять параметры и при необходимости возвращать "неизвестный" результат.
Q. Этот подход имеет штраф за производительность из-за открытия закрытия попытки.
A. Программное обеспечение должно быть легко понятным и модифицированным во-первых. Только после этого можно было подумать о производительности, и только в случае необходимости! и там, где это необходимо! (источник) и многие другие).
PS. Такой подход будет разумным использовать, поскольку отдельный код обработки ошибок из "обычного" принципа кода разумно использовать в некотором месте. Рассмотрим следующий пример:
public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
try {
Result1 result1 = performSomeCalculation(predicate);
Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
Result3 result3 = performThirdCalculation(result2.getSomeProperty());
Result4 result4 = performLastCalculation(result3.getSomeProperty());
return result4.getSomeProperty();
} catch (NullPointerException e) {
return null;
}
}
public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
SomeValue result = null;
if (predicate != null) {
Result1 result1 = performSomeCalculation(predicate);
if (result1 != null && result1.getSomeProperty() != null) {
Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
if (result2 != null && result2.getSomeProperty() != null) {
Result3 result3 = performThirdCalculation(result2.getSomeProperty());
if (result3 != null && result3.getSomeProperty() != null) {
Result4 result4 = performLastCalculation(result3.getSomeProperty());
if (result4 != null) {
result = result4.getSomeProperty();
}
}
}
}
}
return result;
}
PPS. Для тех, кто быстро сжимается (и не так быстро читает документацию), я хотел бы сказать, что в своей жизни я никогда не видел исключения с нулевым указателем (NPE). Но эта возможность была специально разработана разработчиками Java, потому что NPE является подклассом Exception
. У нас есть прецедент в истории Java, когда ThreadDeath
является Error
не потому, что на самом деле это ошибка приложения, а исключительно потому, что он не предназначен для того, чтобы быть пойманным! Сколько NPE подходит для Error
чем ThreadDeath
! Но это не так.
-
Проверьте "Нет данных" только в том случае, если это подразумевает бизнес-логика.
public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
if (personId == null)
return;
DataSource dataSource = appContext.getStuffDataSource();
Person person = dataSource.getPersonById(personId);
if (person != null) {
person.setPhoneNumber(phoneNumber);
dataSource.updatePerson(person);
} else {
Person = new Person(personId);
person.setPhoneNumber(phoneNumber);
dataSource.insertPerson(person);
}
}
а также
public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
if (personId == null)
return;
DataSource dataSource = appContext.getStuffDataSource();
Person person = dataSource.getPersonById(personId);
if (person == null)
throw new SomeReasonableUserException("What are you thinking about ???");
person.setPhoneNumber(phoneNumber);
dataSource.updatePerson(person);
}
Если appContext или dataSource не инициализируется необработанной рабочей средой, NullPointerException убьет текущий поток и будет обработан Thread.defaultUncaughtExceptionHandler (для определения и использования вашего любимого регистратора или другого механизма уведомления). Если не задано, ThreadGroup # uncaughtException будет печатать stacktrace для системного err. Необходимо отслеживать журнал ошибок приложений и открывать проблему Jira для каждого необработанного исключения, которое на самом деле является ошибкой приложения. Программист должен исправить ошибку где-то в файле инициализации.
Ответ 15
Java 7 имеет новый класс утилиты java.util.Objects
, в котором существует метод requireNonNull()
. Все это делает throw NullPointerException
, если его аргумент равен null, но он немного очищает код. Пример:
Objects.requireNonNull(someObject);
someObject.doCalc();
Метод наиболее полезен для проверки непосредственно перед назначением в конструкторе, где каждое его использование может сохранять три строки кода:
Parent(Child child) {
if (child == null) {
throw new NullPointerException("child");
}
this.child = child;
}
становится
Parent(Child child) {
this.child = Objects.requireNonNull(child, "child");
}
Ответ 16
В конечном счете, единственный способ полностью решить эту проблему - использовать другой язык программирования:
- В Objective-C вы можете сделать эквивалент вызова метода на
nil
, и абсолютно ничего не произойдет. Это делает ненужные проверки, но может сделать ошибки намного сложнее для диагностики.
- В Nice, языке Java, существует две версии всех типов: потенциально-нулевая версия и не- null версия. Вы можете использовать методы только для непустых типов. Потенциально-нулевые типы могут быть преобразованы в непустые типы через явную проверку для null. Это значительно облегчает понимание необходимости нулевых проверок и того, где они не являются.
Ответ 17
Обычная "проблема" в Java действительно.
Во-первых, мои мысли об этом:
Я считаю, что плохо "есть" что-то, когда NULL передан, где NULL не является допустимым значением. Если вы не выходите из метода с какой-то ошибкой, значит, в вашем методе ничего не получилось, что неверно. Тогда вы, вероятно, вернете null в этом случае, а в методе получения вы снова проверите значение null, и оно никогда не закончится, и вы получите "if!= Null" и т.д.
Итак, IMHO, null должен быть критической ошибкой, которая предотвращает дальнейшее выполнение (то есть, где null не является допустимым значением).
Как я решаю эту проблему:
Во-первых, я следую этому соглашению:
- Все общедоступные методы /API всегда проверяют свои аргументы для null
- Все частные методы не проверяют значение null, поскольку они управляются методами (просто дайте умереть с исключением nullpointer в случае, если он не обрабатывается выше)
- Единственными другими методами, которые не проверяют значение null, являются утилиты. Они публичные, но если вы их почему-то называете, вы знаете, какие параметры вы передаете. Это похоже на попытку кипятить воду в чайнике, не давая воды...
И, наконец, в коде первая строка общедоступного метода выглядит следующим образом:
ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();
Обратите внимание, что addParam() возвращает self, так что вы можете добавить дополнительные параметры для проверки.
Метод validate()
будет отмечен флажком ValidationException
, если какой-либо из параметров имеет значение null (отмеченный или не отмеченный флажок - это проблема дизайна/вкуса, но мой ValidationException
отмечен).
void validate() throws ValidationException;
Сообщение будет содержать следующий текст, если, например, "планы" имеют значение null:
" Недопустимое значение аргумента null встречается для параметра [планы]"
Как вы можете видеть, второе значение в методе addParam() (строка) необходимо для пользовательского сообщения, потому что вы не можете легко обнаружить имя переменной pass-in, даже с отражением (в любом случае это не относится к этому сообщению)..).
И да, мы знаем, что за пределами этой строки мы больше не будем сталкиваться с нулевым значением, поэтому просто безопасно вызываем методы для этих объектов.
Таким образом, код чист, прост в обслуживании и читаемости.
Ответ 18
Задавая вопрос о том, что вы можете быть заинтересованы в стратегиях обработки ошибок. Архитектор вашей команды должен решить, как работать с ошибками. Существует несколько способов сделать это:
Конечно, посмотрите также на Aspect Oriented Programming - у них есть четкие способы вставить if( o == null ) handleNull()
в ваш байт-код.
Ответ 19
В дополнение к использованию assert
вы можете использовать следующее:
if (someobject == null) {
// Handle null here then move on.
}
Это немного лучше, чем:
if (someobject != null) {
.....
.....
.....
}
Ответ 20
Просто не используйте значение null. Не допускайте этого.
В моих классах большинство полей и локальных переменных имеют значения, отличные от нуля, и я добавляю в код все заявления о заключении (всегда утверждаю) в коде, чтобы убедиться, что это принудительно (поскольку оно более кратким и более выразительным чем позволить ему появиться как NPE, а затем разрешить номер строки и т.д.).
Как только я принял эту практику, я заметил, что проблемы, похоже, исправились. Вы случайно поймали вещи в процессе разработки и осознали, что у вас слабое место.. и что еще более важно.. он помогает инкапсулировать проблемы с различными модулями, разные модули могут "доверять" друг другу и не более засорять код с конструкциями if = null else
!
Это защитное программирование и в конечном итоге приводит к значительному улучшению кода. Всегда дезинфицируйте данные, например. здесь, применяя жесткие стандарты, и проблемы уходят.
class C {
private final MyType mustBeSet;
public C(MyType mything) {
mustBeSet=Contract.notNull(mything);
}
private String name = "<unknown>";
public void setName(String s) {
name = Contract.notNull(s);
}
}
class Contract {
public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }
}
Контракты похожи на тесты на мини-модуле, которые всегда работают, даже в производстве, и когда что-то не удается, вы знаете, почему, а не случайный NPE, вам нужно как-то понять.
Ответ 21
Гува, очень полезная базовая библиотека от Google, имеет хороший и полезный API, чтобы избежать нулей. Я считаю UsingAndAvoidingNullExplained очень полезным.
Как объясняется в wiki:
Optional<T>
- это способ замены нулевой ссылки T на ненулевое значение. Необязательный может либо содержать ненулевую ссылку T (в этом случае мы говорим, что ссылка "присутствует" ), или она может содержать ничего (в этом случае мы говорим, что ссылка "отсутствует" ). Это никогда сказал, что "содержит null".
Использование:
Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5
Ответ 22
Это очень распространенная проблема для каждого разработчика Java. Таким образом, в Java 8 есть официальная поддержка для решения этих проблем без загроможденного кода.
Java 8 представила java.util.Optional<T>
. Это контейнер, который может содержать или не содержать ненулевое значение. Java 8 предоставил более безопасный способ обработки объекта, значение которого может быть нулевым в некоторых случаях. Это вдохновлено идеями Haskell и Scala.
В двух словах класс Option включает методы для явного рассмотрения случаев, когда значение присутствует или отсутствует. Однако преимущество по сравнению с нулевыми ссылками заключается в том, что класс Optional <T> заставляет вас думать о случае, когда значение отсутствует. Как следствие, вы можете предотвратить непреднамеренные исключения нулевого указателя.
В приведенном выше примере у нас есть домашняя служба factory, которая возвращает дескриптор нескольких устройств, доступных в доме. Но эти услуги могут быть или не быть доступными/функциональными; это означает, что это может привести к исключению NullPointerException. Вместо того, чтобы добавлять условие null if
перед использованием какой-либо службы, включите ее в Необязательный <Service> .
ОТКЛЮЧЕНИЕ К ОПЦИИ <T>
Рассмотрим способ получения ссылки службы из factory. Вместо того, чтобы возвращать служебную ссылку, оберните ее опцией. Это позволяет пользователю API знать, что возвращенная услуга может быть или не быть доступной/функциональной, использовать защищенно
public Optional<Service> getRefrigertorControl() {
Service s = new RefrigeratorService();
//...
return Optional.ofNullable(s);
}
Как вы видите, Optional.ofNullable()
обеспечивает простой способ получения ссылки. Есть еще один способ получить ссылку Необязательный, либо Optional.empty()
, и Optional.of()
. Один для возврата пустого объекта вместо того, чтобы перенастроить нуль, а другой - для обертывания объекта, не подлежащего обнулению, соответственно.
ТАК КАК ЭТО ТОГО, ЧТОБЫ ИЗБЕЖАТЬ СЛЕДУЮЩЕЙ ПРОВЕРКИ?
После того, как вы завернули ссылочный объект, необязательно предоставляет множество полезных методов для вызова методов на завернутой ссылке без NPE.
Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);
Необязательный .ifPresent вызывает данный потребитель с ссылкой, если он является ненулевым значением. В противном случае он ничего не делает.
@FunctionalInterface
public interface Consumer<T>
Представляет операцию, которая принимает один входной аргумент и не возвращает результат. В отличие от большинства других функциональных интерфейсов, потребитель, как ожидается, будет работать через побочные эффекты.
Это так чисто и легко понять. В приведенном выше примере кода HomeService.switchOn(Service)
вызывается, если ссылка необязательного удержания не равна нулю.
Мы используем тернарный оператор очень часто для проверки нулевого условия и возврата альтернативного значения или значения по умолчанию. Опция предоставляет другой способ обработки одного и того же условия без проверки нулевого значения. Необязательный .orElse(defaultObj) возвращает defaultObj, если опция имеет нулевое значение. Позвольте использовать это в нашем примере кода:
public static Optional<HomeServices> get() {
service = Optional.of(service.orElse(new HomeServices()));
return service;
}
Теперь HomeServices.get() делает то же самое, но лучше. Он проверяет, уже ли инициализирована услуга. Если это тогда, верните то же самое или создайте новую новую услугу. Необязательный <T> .orElse(T) помогает вернуть значение по умолчанию.
Наконец, вот наш NPE, а также нулевой код без кода:
import java.util.Optional;
public class HomeServices {
private static final int NOW = 0;
private static Optional<HomeServices> service;
public static Optional<HomeServices> get() {
service = Optional.of(service.orElse(new HomeServices()));
return service;
}
public Optional<Service> getRefrigertorControl() {
Service s = new RefrigeratorService();
//...
return Optional.ofNullable(s);
}
public static void main(String[] args) {
/* Get Home Services handle */
Optional<HomeServices> homeServices = HomeServices.get();
if(homeServices != null) {
Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
refrigertorControl.ifPresent(HomeServices::switchItOn);
}
}
public static void switchItOn(Service s){
//...
}
}
Полный пост NPE, а также Null check-free code... Действительно?.
Ответ 23
Мне нравятся статьи из Nat Pryce. Вот ссылки:
В статьях есть также ссылка на репозиторий Git для Java Maybe Type, который мне интересен, но я не думаю, что он один может уменьшить
проверка кода раздувания. После нескольких исследований в Интернете, я думаю, что != Null раздутие кода может быть уменьшено главным образом путем тщательного проектирования.
Ответ 24
Я пробовал NullObjectPattern
, но для меня это не всегда лучший способ. Иногда бывает, когда "нет действия" не подходит.
NullPointerException
- исключение Runtime, которое означает, что разработчики не работают, и с достаточным опытом он сообщает вам, где именно ошибка.
Теперь ответ:
Попробуйте сделать все ваши атрибуты и их аксессоры доступными как можно более частными или вообще не подвергать их клиентам. Разумеется, вы можете иметь значения аргументов в конструкторе, но, уменьшая область действия, которую вы не позволяете классу клиента передавать недопустимое значение. Если вам нужно изменить значения, вы всегда можете создать новый object
. Вы проверяете значения в конструкторе только один раз, а в остальных методах вы можете быть почти уверены, что значения не равны нулю.
Конечно, опыт - лучший способ понять и применить это предложение.
Byte!
Ответ 25
Вероятно, лучшей альтернативой Java 8 или новее является использование класса Optional
.
Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);
Это особенно удобно для длинных цепочек возможных нулевых значений. Пример:
Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
.map(f -> f.getBar())
.map(b -> b.getBaz())
.map(b -> b.getInt());
Пример того, как выбрасывать исключение в null:
Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);
Java 7 представила метод Objects.requireNonNull
, который может быть полезен, когда что-то нужно проверить для непустоты. Пример:
String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();
Ответ 26
Могу ли я ответить на него в более общем плане?
Мы обычно сталкиваемся с этой проблемой, когда методы получают параметры так, как мы не ожидали (плохой вызов метода - ошибка программиста). Например: вы ожидаете получить объект, вместо этого вы получите нуль. Вы ожидаете получить String с хотя бы одним символом, вместо этого вы получите пустую строку...
Таким образом, нет никакой разницы между:
if(object == null){
//you called my method badly!
}
или
if(str.length() == 0){
//you called my method badly again!
}
Они оба хотят удостовериться, что мы получили действительные параметры, прежде чем будем выполнять какие-либо другие функции.
Как упоминалось в некоторых других ответах, чтобы избежать описанных выше проблем, вы можете следовать шаблону Дизайн по контракту. См. http://en.wikipedia.org/wiki/Design_by_contract.
Чтобы реализовать этот шаблон в java, вы можете использовать аннотации ядра java, такие как javax.annotation.NotNull или использовать более сложные библиотеки, такие как Hibernate Validator.
Только образец:
getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)
Теперь вы можете безопасно развить основную функцию вашего метода без необходимости проверки входных параметров, они защищают ваши методы от неожиданных параметров.
Вы можете сделать еще один шаг и убедиться, что в приложении могут быть созданы только действительные pojos. (образец с сайта валидатора спящего режима)
public class Car {
@NotNull
private String manufacturer;
@NotNull
@Size(min = 2, max = 14)
private String licensePlate;
@Min(2)
private int seatCount;
// ...
}
Ответ 27
Я очень пренебрегаю ответами, которые предполагают использование нулевых объектов в каждой ситуации. Этот шаблон может разорвать контракт и похоронить проблемы глубже и глубже, вместо того, чтобы их решать, не говоря уже о том, что использование неправомерно создаст еще одну кучу кода шаблона, который потребует дальнейшего обслуживания.
В действительности, если что-то, возвращаемое из метода, может быть нулевым, а вызывающий код должен принять решение, должен быть более ранний вызов, который обеспечивает состояние.
Также имейте в виду, что шаблон нулевого объекта будет голоден, если использовать его без осторожности. Для этого - экземпляр NullObject должен делиться между владельцами, а не быть экземпляром unigue для каждого из них.
Также я бы не рекомендовал использовать этот шаблон, где тип должен представлять собой примитивное представление типа, например, математические объекты, которые не являются скалярами: векторами, матрицами, комплексными числами и объектами POD (обычные старые данные), которые означают для хранения состояния в виде встроенных типов Java. В последнем случае вы в конечном итоге вызываете методы getter с произвольными результатами. Например, что должен вернуть метод NullPerson.getName()?
Стоит рассмотреть такие случаи, чтобы избежать абсурдных результатов.
Ответ 28
- Не инициализировать переменные до нуля.
- Если (1) невозможно, инициализируйте все коллекции и массивы пустым коллекциям/массивам.
Выполняйте это в своем собственном коде, и вы можете избежать!= нулевые проверки.
В большинстве случаев нулевые проверки, похоже, защищают циклы над коллекциями или массивами, поэтому просто инициализируйте их пустыми, вам не понадобятся никакие проверки нуля.
// Bad
ArrayList<String> lemmings;
String[] names;
void checkLemmings() {
if (lemmings != null) for(lemming: lemmings) {
// do something
}
}
// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = {};
void checkLemmings() {
for(lemming: lemmings) {
// do something
}
}
В этом есть крошечные накладные расходы, но это стоит того, чтобы сделать более чистый код и меньше NullPointerExceptions.
Ответ 29
Это самая распространенная ошибка для большинства разработчиков.
У нас есть несколько способов справиться с этим.
Подход 1:
org.apache.commons.lang.Validate //using apache framework
notNull (объект объекта, сообщение String)
Подход 2:
if(someObject!=null){ // simply checking against null
}
Подход 3:
@isNull @Nullable // using annotation based validation
Подход 4:
// by writing static method and calling it across whereever we needed to check the validation
static <T> T isNull(someObject e){
if(e == null){
throw new NullPointerException();
}
return e;
}
Ответ 30
public static <T> T ifNull(T toCheck, T ifNull) {
if (toCheck == null) {
return ifNull;
}
return toCheck;
}