Java equals null exception

Приемы и советы. Как избежать NullPointerException в Java приложениях

Java-университет

Приемы и советы. Как избежать NullPointerException в Java приложениях - 1

Сегодня я покажу вам простые приемы того как избежать NullPointerException в ваших приложениях. Им легко следовать, но при этом они заметно повышают надежность и качество вашего кода. Более того, по моему опыту, один первый совет окажет заметное влияние на качество вашего кода. Если вы знаете какие-то еще приемы программирования на Java, не стесняйтесь делиться ими в комментариях.

Вызывайте методы equals() и equalsIgnoreCase() у известной строки литерала, а не у неизвестного объекта

Всегда вызывайте метод equals() у известной строки, про которую вы знаете, что она не null . Метод equals() симметричен, то есть вызов a.equals(b) и b.equals(a) даст одинаковый результат (если a и b не null ), и по этой причине многие программисты не уделяют внимание тому, у каких объектов вызывается equals() , у a или у b . Одним из побочных эффектов этого становится NullPointerException, в случае если метод вызывается у null .

 Object unknownObject = null; //плохой способ - может вызвать NullPointerException if(unknownObject.equals("knownObject")) < System.err.println("This may result in NullPointerException if unknownObject is null"); >//правильный способ - исключение NullPointerException не возникнет, даже если unknownObject null if("knownObject".equals(unknownObject))

Это был самый простой совет, как избежать NullPointerException, но он один приводит к громадным улучшениям, поскольку метод equals() встречается повсеместно.

Читайте также:  Scrappy для python 3

Выбирайте valueOf(), а не toString(), в случаях, когда оба дают один результат

Поскольку вызов toString() у ссылки со значением null вызывает исключение NullPointerException, лучше использовать вызов valueOf() , когда мы можем получить тот же результат, поскольку вызов valueOf() от null возвращает null . Особенно это касается классов оберток, таких как Integer , Float , Double или BigDecimal .

 BigDecimal bd = getPrice(); System.out.println(String.valueOf(bd)); //не выбрасывает NPE System.out.println(bd.toString()); //выбрасывает "Exception in thread "main" java.lang.NullPointerException" 

Используйте null -безопасные методы и библиотеки

Существует множество опенсорсных библиотек, которые берут на себе тяжелое бремя проверки на null . Одна из наиболее распространенных — StringUtils от Apache Commons. Используя такие методы как StringUtils.isBlank() , isNumeric() , isWhiteSpace() и т.д. вы можете не беспокоится о возникновении NullPointerException.

 //Методы StringUtils являются null-безопасными, они не вызовут NullPointerException System.out.println(StringUtils.isEmpty(null)); System.out.println(StringUtils.isBlank(null)); System.out.println(StringUtils.isNumeric(null)); System.out.println(StringUtils.isAllUpperCase(null)); 

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

Старайтесь не возвращать null из метода, лучше верните пустую коллекцию

Это еще один хороший совет по программированию на Java, который Джошуа Блох описывает в своей книге «Java. Эффективное программирование». Возвращая пустые коллекции или массивы, убедитесь, что при вызове базовых методов вроде size() или length() не возникнет NullPointerException. В классе Collections специально объявлены удобные реализации пустых списков, множеств и словарей: Collections.EMPTY_LIST , Collections.EMPTY_SET and Collections.EMPTY_MAP . Например:

 public List getOrders(Customer customer)

Аналогично можно использовать Collections.EMPTY_SET и Collections.EMPTY_MAP вместо того, чтобы возвращать null .

Используйте аннотации @NotNull и @Nullable

В описании ваших методов вы можете определить соглашение null -безопасности методов, используя аннотации @NotNull и @Nullable чтобы показать может метод возвращать null или нет. Современные компиляторы и IDE могут использовать эти аннотации для анализы вашего кода и выдавать соответствующие советы, например, о пропущенной проверке на null , или наоборот, о возможности убрать лишнюю проверку, засоряющую код. Такие аннотации, например, поддерживают IntelliJ IDE и FindBugs, кроме этого они входят в JSR 305. Но даже если ваша IDE и не поддерживает такие аннотации, они сами по себе будут хорошей документацией. Глядя на @NotNull и @Nullable программисту будет проще понять где нужно добавлять проверку на null , а где — нет. Это, кстати, довольно новая среди Java программистов практика, и должно пройти время, чтобы она распространилась.

Избегайте ненужной автоупаковки и автораспаковки в вашем коде

Мало того, что это ведет к созданию лишних временных объектов, автоупаковка еще может вызывать NullPointerException, если класс обертки — null . Например, следующий код «свалится» в NullPointerException, если запись о человеке не содержит телефона и возвращает null .

 Person ram = new Person("ram"); int phone = ram.getPhone(); 

При использовании автоупаковки или автораспаковки не только равенства, но и неравенства < , >> могут приводить к NullPointerException.

Следуйте соглашениям и определяйте разумные значения по-умолчанию.

Один из лучших способов избежать NullPointerException в Java — правильное объявление соглашений о кодировании и следование им. Большинство исключений NullPointerException возникают тогда, когда объект пытаются создать, не обладая всеми необходимыми для него данными и зависимостями. Запретив создание незавершенных объектов, изящно отклоняя подобные запросы, вы избавите себя в будущем от большого числа NullPointerException. Аналогично, если вы разрешаете создание объекта, то вы должны подобрать разумное значение по-умолчанию. Например, объект класса Employee не может быть создан без имени и id , но может не иметь номер телефона. В таком случае у объектов Employee , лишенных номера, вместо null может возвращаться ноль. Хотя подобное поведение объекта стоит заранее продумать — быть может, что проще будет проверить на null , нежели звонить по несуществующему номеру. В таком случае наличие дополнительных условий на то, какие поля null , а какие — нет, поможет принять правильное решение. В целом выбор между немедленным падением программы или присвоением null — это важное решение в проектировании, и сделав выбор вы должны ему последовательно следовать.

Задавайте ограничения на уровне СУБД

Используя базу данных для хранения объектов вашей программы, таких как покупатели (Customers) или заказы (Orders), будет разумно задать » null -овость» ваших объектов на уровне СУБД, при помощи соответствующих ограничений на таблицы. Базы данных часто содержат информацию из разных источников, и введение проверок на пропущенные значения повысит целостность ваших данных. Кроме этого наличие проверок на null на уровне СУБД, уменьшит их в вашем Java коде: загружая данные из БД в Java-объекты, вы можете быть уверены в их наличии и убрать лишние » != null » из кода программы.

Используйте шаблон Null-объекта

Создание специальных Null -объектов — еще один способ избежать NullPointerExcpetion в Java. Предположим, что какой-то метод в нашем приложении возвращает объект, с которым впоследствии работает программа, вызавая его методы. Например, метод Collection.iterator() возвращает объект класса Iterator , который используется для прохода по коллекции. Тогда если у изначального объекта нет никакого итератора, вместо null можно вернуть специальный Null -объект, у которого есть метод hasNext() , всегда возвращающий false . Вот и все дорогой читатель, на этом я заканчиваю свои советы по избавлению Java программ от ошибок NullPointerException. Вы оцените, на сколько могут быть полезны эти простые и не обременительные правила. Напоминаю, что если вы хотите поделиться какими-то еще приемами по NullPointerException в программах на Java, не стесняйтесь делать это в комментариях. Переведено специально для студентов JavaRush. Оригинал

Источник

Как понять NullPointerException

Эта простая статья скорее для начинающих разработчиков Java, хотя я нередко вижу и опытных коллег, которые беспомощно глядят на stack trace, сообщающий о NullPointerException (сокращённо NPE), и не могут сделать никаких выводов без отладчика. Разумеется, до NPE своё приложение лучше не доводить: вам помогут null-аннотации, валидация входных параметров и другие способы. Но когда пациент уже болен, надо его лечить, а не капать на мозги, что он ходил зимой без шапки.

  1. Его кинули с помощью throw
  2. Кто-то кинул null с помощью throw
  3. Кто-то пытается обратиться по null-ссылке
  1. Вызов нестатического метода класса
  2. Обращение (чтение или запись) к нестатическому полю
  3. Обращение (чтение или запись) к элементу массива
  4. Чтение length у массива
  5. Неявный вызов метода valueOf при анбоксинге (unboxing)
 1: class Data < 2: private String val; 3: public Data(String val) 4: public String getValue() 5: > 6: 7: class Formatter < 8: public static String format(String value) < 9: return value.trim(); 10: >11: > 12: 13: public class TestNPE < 14: public static String handle(Formatter f, Data d) < 15: return f.format(d.getValue()); 16: >17: >
Exception in thread "main" java.lang.NullPointerException at TestNPE.handle(TestNPE.java:15)

В чём причина исключения — в f, d или d.val? Нетрудно заметить, что f в этой строке вообще не читается, так как метод format статический. Конечно, обращаться к статическому методу через экземпляр класса плохо, но такой код встречается (мог, например, появиться после рефакторинга). Так или иначе значение f не может быть причиной исключения. Если бы d был не null, а d.val — null, тогда бы исключение возникло уже внутри метода format (в девятой строчке). Аналогично проблема не могла быть внутри метода getValue, даже если бы он был сложнее. Раз исключение в пятнадцатой строчке, остаётся одна возможная причина: null в параметре d.

 1: class Formatter < 2: public String format(String value) < 3: return "["+value+"]"; 4: >5: > 6: 7: public class TestNPE < 8: public static String handle(Formatter f, String s) < 9: if(s.isEmpty()) < 10: return "(none)"; 11: >12: return f.format(s.trim()); 13: > 14: >
Exception in thread "main" java.lang.NullPointerException at TestNPE.handle(TestNPE.java:12)

Теперь метод format нестатический, и f вполне может быть источником ошибки. Зато s не может быть ни под каким соусом: в девятой строке уже было обращение к s. Если бы s было null, исключение бы случилось в девятой строке. Просмотр логики кода перед исключением довольно часто помогает отбросить некоторые варианты.

С логикой, конечно, надо быть внимательным. Предположим, условие в девятой строчке было бы написано так:

Теперь в самой строчке обращения к полям и методам s нету, а метод equals корректно обрабатывает null, возвращая false, поэтому в таком случае ошибку в двенадцатой строке мог вызвать как f, так и s. Анализируя вышестоящий код, уточняйте в документации или исходниках, как используемые методы и конструкции реагируют на null. Оператор конкатенации строк +, к примеру, никогда не вызывает NPE.

Вот такой код (здесь может играть роль версия Java, я использую Oracle JDK 1.7.0.45):

 1: import java.io.PrintWriter; 2: 3: public class TestNPE < 4: public static void dump(PrintWriter pw, MyObject obj) < 5: pw.print(obj); 6: >7: >
Exception in thread "main" java.lang.NullPointerException at java.io.PrintWriter.write(PrintWriter.java:473) at java.io.PrintWriter.print(PrintWriter.java:617) at TestNPE.dump(TestNPE.java:5)

В параметре pw не может быть null, иначе нам не удалось бы войти в метод print. Возможно, null в obj? Легко проверить, что pw.print(null) выводит строку «null» без всяких исключений. Пойдём с конца. Исключение случилось здесь:

472: public void write(String s)

В строке 473 возможна только одна причина NPE: обращение к методу length строки s. Значит, s содержит null. Как так могло получиться? Поднимемся по стеку выше:

616: public void print(Object obj)

В метод write передаётся результат вызова метода String.valueOf. В каком случае он может вернуть null?

public static String valueOf(Object obj)

Единственный возможный вариант — obj не null, но obj.toString() вернул null. Значит, ошибку надо искать в переопределённом методе toString() нашего объекта MyObject. Заметьте, в stack trace MyObject вообще не фигурировал, но проблема именно там. Такой несложный анализ может сэкономить кучу времени на попытки воспроизвести ситуацию в отладчике.

Не стоит забывать и про коварный автобоксинг. Пусть у нас такой код:

Exception in thread "main" java.lang.NullPointerException at TestNPE.getCount(TestNPE.java:3)

На первый взгляд единственный вариант — это null в параметре obj. Но следует взглянуть на класс MyContainer:

import java.util.List; public class MyContainer < Listelements; public MyContainer(List elements) < this.elements = elements; >public Integer getCount() < return elements == null ? null : elements.size(); >>

Мы видим, что getCount() возвращает Integer, который автоматически превращается в int именно в третьей строке TestNPE.java, а значит, если getCount() вернул null, произойдёт именно такое исключение, которое мы видим. Обнаружив класс, подобный классу MyContainer, посмотрите в истории системы контроля версий, кто его автор, и насыпьте ему крошек под одеяло.

Помните, что если метод принимает параметр int, а вы передаёте Integer null, то анбоксинг случится до вызова метода, поэтому NPE будет указывать на строку с вызовом.

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

Источник

Оцените статью