Разработка программ. Мои заметки.

January 7, 2017 at 19:05

NullPointerException в Java. The Billion Dollar Mistake.

2. Tony Hoare: “Null References: The Billion Dollar Mistake”.

Специального типа данных, называемого словами указатель или ссылка (как в C++), в языке Java - нет. Но в java любая переменная, которая не относится к встроенному примитивному типу данных (а это: byte, short, int, long, float, double, char и boolean) содержит именно адрес памяти, по которому расположена нужная нам информация и, таким образом, по своей сути является ссылкой. Или указателем, как вам больше нравится.

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


    SomeClass someClass = new SomeClass();
    ...
    someClass = new SomeClass();

В данном примере переменная someClass хранит адрес первого создаваемого объекта и затем адрес второго создаваемого объекта. До того как мы присвоим переменной адрес первого объекта она ничего не хранит. “Ничего”, это в Java обозначается словом null и называется нулевой ссылкой. Любая такая переменная изначально хранит нулевую ссылку. Где бы она не была объявлена. Как локальная, как статическая, как поле класса. Если ей не присваивается сразу результат операции new, то там хранится null. Подробнее о специфике Java ссылок можно почитать в любом учебнике по языку или, к примеру, на сайте Евгения Матюшкина.

На первый взгляд ничего странного тут нет. Объявили переменную - в неё автоматически записался null. Присвоили ей результат операции new — она стала хранить адрес объекта того типа, который указан в типе данных переменной. Или его потомка. Сейчас это уже кажется естественным, но первым концепцию нулевой ссылки предложил Тони Хоар (Tony Hoare ака Charles Antony Richard Hoare). Пишет, что было это в 1965 году. Тони Хоар тогда проектировал свой объектно-ориентированный язык Algol W. В нём он и ввёл понятие ссылки на структуру и предложил хранить там некий абстрактный ноль/null, если ссылка пока или уже ни на что не указывает. Впоследствии, Хоар был очень расстроен этим проектным решением. Он назвал его “…ошибкой на миллиард долларов…”

Вся проблема в том, что переменная, предназначенная для хранения ссылки на некоторый объект, не может быть использована для доступа к информации объекта, если в ней сейчас хранится null. Это очевидно. Если адреса объекта нет (null), то и достать с помощью этой переменной мы ничего не можем. При попытке это сделать, мы получаем любимый всеми нами NullPointerException. Другими словами — программа генерирует исключительную ситуацию, называемую, исключительной ситуацией выполнения, возникшей из-за попытки получить значение по адресу, который содержит null. И, порой, очень сложно найти причину, почему ссылка содержит нулевой адрес. Быть может где-то ранее не удалось найти некий объект в методе, или алгоритм вернул null по причине того, что входные данные были некорректны.

Как бы там ни было, но программисты стали легко и просто, я бы сказал — походя, возвращать null из своих методов при любой неудачной работе их куска кода. А другие программисты, которые используют код тех первых программистов, стали вынуждены тихо шалеть и постоянно проверять — а не null ли у нас в ответе работы API метода? Или получать волшебный NullPointerException. Даже один и тот же программист вполне мог забыть, что его метод иногда возвращает null. Извечный рефрен жизни. Так как же с этим бороться?

Вот казалось бы, а что же ещё должна хранить ссылка, если она сейчас ни на что не указывает? Согласитесь, хороший вопрос. Вроде ответ один — она должна хранить этот null. Нулевую ссылку. Тогда в чём проблема? А проблема в том, что бы построить синтаксис языка программирования таким образом, что бы компилятор мог отслеживать факт того, что переменная-ссылка в текущей точке программы потенциально может иметь значение null. И всячески информировать об этом программиста ещё на этапе выбора и использования в коде некоего метода API. Заставлять его принимать какое-то решение.

Вне зависимости от синтаксиса языка, всегда есть прямой и незамысловатый подход — проверять любую получаемую ссылку на предмет того не равна ли она null. Решение хорошее, проверенное временем и гарантированно работающее. Старательно проверять всё на null. Весь ваш код будет покрыт этим забавным помётом в духе: if( myObject != null ) {...}. Производительность, разумеется, снижается. Куча проверок. Особенно “стильно” это выглядит когда мы обращаемся к полю некоторого другого поля, которое также есть чьё-то ещё поле. И каждое из них, потенциально, может быть null. А значит “споткнуться” мы можем на любом из них.

Автор — Владимир Рыбов