Выпущенный 17 марта 2020 года Java официально выпустил JDK 14, который теперь доступен для загрузки. В JDK 14 есть 16 новых функций, в этой статье в основном представлена одна из них: JEP 358: Helpful NullPointerExceptions.
Что не так с нулем?
Для Java-программистов null — это головная боль. Часто притеснением NullPointerException (NullPointerException). Считается, что многие программисты особенно боятся NPE в программе, потому что подобного рода исключения часто сопровождаются неожиданным срабатыванием кода.
В языках программирования нулевая ссылка — это концепция, аналогичная нулевому указателю, переменной, которая была объявлена, но не ссылается на действительный объект.
Нулевые ссылки и NPE были включены в Java 1, но на самом деле нулевые ссылки были изобретены великим компьютерщиком Тони Хоаром еще в 1965 году как часть языка программирования ALGOL W.
В 1965 году британский ученый-компьютерщик по имени Тони Хоар придумал идею нулевых ссылок при разработке языка ALGOL W. ALGOL W был одним из первых типизированных языков, в котором записи размещались в куче. Хоар решил использовать нулевые ссылки «только потому, что это так легко реализовать». Хотя его первоначальным намерением было «убедиться, что все ссылки на ссылки абсолютно безопасны с помощью механизма автоматического обнаружения компилятора», он решил дать зеленый свет нулевой ссылке, потому что считал ее «несуществующим значением». модель.
Но в 2009 году, много лет спустя, он начал сожалеть о принятом им решении, назвав его «ошибкой на миллиард долларов». На самом деле заявление Хора недооценивает затраты миллионов программистов на исправление нулевых ссылок за последние пятьдесят лет. Потому что большинство современных языков программирования, появившихся после ALGOL W, включая Java, были спроектированы таким же образом, и причиной этого было сохранение совместимости со старыми языками, или, как однажды заявил Хоар, «просто потому, что это проще сделать». "
Я полагаю, что многие Java-программисты испытывают такое же отвращение к null и NPE, потому что это приносит всевозможные проблемы (из "Java 8 Actual Combat"). как:
- Это источник ошибок. NullPointerException является наиболее типичным исключением в текущей разработке программ Java. Это раздует ваш код.
- Он заполняет ваш код глубоко вложенными проверками null, а читабельность кода ужасна.
- Это бессмысленно само по себе. null сам по себе не имеет семантики, тем более что он представляет собой неправильный способ моделирования отсутствующих значений переменных в статически типизированных языках.
- Это разрушает философию Java. Java всегда старалась не информировать программистов об указателях, за одним исключением: нулевые указатели.
- Это открывает дыру в системе типов Java. null не принадлежит ни к какому типу, что означает, что его можно присвоить переменным любого ссылочного типа. Это может вызвать проблемы, потому что, когда переменная передается в другую часть системы, вы не будете знать, к какому типу изначально была привязана нулевая переменная.
Как другие языки решают проблемы NPE
Мы знаем, что помимо языка Java существует множество других объектно-ориентированных языков, так как же решить проблему NPE в некоторых других языках?
Например, с помощью оператора безопасной навигации в Groovy можно получить доступ к переменным, которые могут иметь значение null:
def carInsuranceName = person?.car?.insurance?.name
Оператор безопасной навигации Groovy позволяет избежать исключений NullPointerException при доступе к переменным, которые могут быть пустыми ссылками, передавая пустые ссылки вниз по цепочке вызовов, когда переменная в цепочке вызовов встречает значение null, возвращая значение null.
На самом деле эта функция рассматривалась как добавление аналогичной функции, но позже от нее отказались.
Кроме того, в Haskell и Scala есть аналогичные альтернативы, такие как тип Maybe в Haskell и Option[T] в Scala.
В Kotlin его система типов строго различает, может ли ссылка содержать null или нет. То есть, может ли переменная быть нулевой, должно быть объявлено явно.Для переменных, допускающих нуль, она должна быть нулевой при доступе к своим членам, иначе она не может быть скомпилирована:
var a: String = "abc"
a = null // 编译错误
Если допускается обнуляемость, вы можете объявить обнуляемую строку, написав String?:
var b: String? = "abc" //String? 表示该 String 类型变量可为空
b = null // 编译通过
видеть это? , вы обнаружили, что это немного похоже на Groovy? Однако есть еще некоторые отличия, которые мы не будем здесь раскрывать.
Что ж, вернемся к книге, давайте посмотрим, какие усилия приложил язык Java для NPE как языка, занимающего первое место в списке языков программирования TOIBE!
Что сделали Java
Был улучшен для нуля, и NPE все еще прилагаются некоторые усилия.
Необязательный впервые был предоставлен в Java 8. Фактически, до появления Java 8 библиотека Google Guava впервые предоставила необязательный интерфейс, чтобы сделать нулевой отказ быстрым.
Необязательный объект является оболочкой для объекта, который может иметь значение null. Объект Optional содержит методы для явной обработки наличия или отсутствия значения. Класс Optional заставляет вас думать об отсутствии значения, тем самым избегая потенциального исключения нулевого указателя.
Но цель разработки класса Optional не в том, чтобы полностью заменить null, а в том, чтобы разработать более понятный API. С опциональным вы знаете из сигнатуры метода, что функция может вернуть отсутствующее значение, что вынуждает вас иметь дело с этими случаями отсутствия значения.
Что касается использования опциона, то он не является предметом этой статьи, поэтому я не буду здесь подробно о нем рассказывать.Я часто использую опционал в сочетании с Stream в повседневной разработке, который довольно прост в использовании.
Еще одна вещь, о которой стоит упомянуть, это улучшение NPE в недавно выпущенном JDK 14 (17 марта 2020 г.). Это JEP 358: полезные исключения NullPointerException
Более полезный NPE
В JDK 14 есть улучшение NEP. Поскольку NPE пока нельзя избежать, пусть он будет более полезным для разработчиков.
Каждый разработчик Java сталкивался с NullPointerExceptions (NPE). Поскольку NPE могут возникать практически в любом месте программы, попытки их отлова и восстановления часто нецелесообразны. В результате разработчики часто полагаются на JVM, чтобы определить, где на самом деле происходит NPE. Например, предположим, что в этом коде встречается NPE:
a.i = 99;
JVM распечатает метод, имя файла и номер строки, которые вызвали NPE:
Exception in thread "main" java.lang.NullPointerException
at Prog.main(Prog.java:5)
Используя приведенную выше информацию о стеке, разработчик может найти строку a.i = 99 и сделать вывод, что a должно быть нулевым.
Однако в более сложном коде невозможно определить, какая переменная имеет значение null, без использования отладчика. Предположим, что в этом коде встречается NPE:
a.b.c.i = 99;
Нельзя определить в конце концов, является A или B или C во время выполнения - это нулевое значение.
Однако после JDK14 эта дилемма решена.
В JDK14 при попытке применить нулевой объект во время выполнения JVM по-прежнему выдает NullPointerException (NPE).Кроме того, анализируя инструкции байт-кода программы, JVM точно определяет, какая переменная имеет значение null, указывается в информации о стеке.
В JDK 14, если NPE встречается с a.i = 99 выше, стек будет напечатан следующим образом:
Exception in thread "main" java.lang.NullPointerException:
Cannot assign field "i" because "a" is null
at Prog.main(Prog.java:5)
Если b в a.b.c.i = 99; равно null, что приводит к нулевому указателю, будет напечатана следующая информация о стеке:
Exception in thread "main" java.lang.NullPointerException:
Cannot read field "c" because "a.b" is null
at Prog.main(Prog.java:5)
Видно, что стек четко указывает, какой объект является нулевым, что вызвало NPE.Таким образом, как только NPE возникает в приложении, разработчик может немедленно найти объект в коде, который вызван нулем, через информацию стека. .
Это небольшое улучшение в JDK, но оно очень удобно для разработчиков. Очень надеюсь, что этих маленьких и красивых изменений будет все больше и больше в JDK.
Ссылка:
«Ява 8 в действии»