NullAway — это инструмент, помогающий устранить NullPointerException
(NPE) в вашем Java-коде. Чтобы использовать NullAway, сначала добавьте аннотации @Nullable
в свой код везде, где поле, параметр метода или возвращаемое значение могут иметь null
. Учитывая эти аннотации, NullAway выполняет серию локальных проверок на основе типов, чтобы гарантировать, что любой указатель, который разыменовывается в вашем коде, не может иметь null
. NullAway аналогичен проверке допустимости значений NULL на основе типов в языках Kotlin и Swift, а также средствам проверки NULL Checker Framework и Eradicate для Java.
NullAway работает быстро . Он создан как плагин для Error Prone и может работать на каждой сборке вашего кода. По нашим измерениям, затраты времени сборки при запуске NullAway обычно составляют менее 10%. NullAway также практичен : он не предотвращает все возможные NPE в вашем коде, но перехватывает большинство NPE, которые мы наблюдали в рабочей среде, одновременно налагая разумную нагрузку на аннотации, обеспечивая отличную «отдачу от затраченных средств».
NullAway требует, чтобы вы создавали свой код с помощью Error Prone версии 2.14.0 или выше. Инструкции по началу работы с Error Prone и интеграции с вашей системой сборки см. в документации Error Prone. В приведенных ниже инструкциях предполагается, что вы используете Gradle; см. документацию для обсуждения других систем сборки.
Чтобы интегрировать NullAway в проект Java, отличный от Android, добавьте в файл build.gradle
следующее:
plugins {
// we assume you are already using the Java plugin
id " net.ltgt.errorprone " version " <plugin version> "
}
dependencies {
errorprone " com.uber.nullaway:nullaway:<NullAway version> "
// Some source of nullability annotations; JSpecify recommended,
// but others supported as well.
api " org.jspecify:jspecify:1.0.0 "
errorprone " com.google.errorprone:error_prone_core:<Error Prone version> "
}
import net.ltgt.gradle.errorprone.CheckSeverity
tasks . withType( JavaCompile ) {
options . errorprone {
check( " NullAway " , CheckSeverity . ERROR )
option( " NullAway:AnnotatedPackages " , " com.uber " )
}
// Include to disable NullAway on test code
if (name . toLowerCase() . contains( " test " )) {
options . errorprone {
disable( " NullAway " )
}
}
}
Давайте пройдемся по этому сценарию шаг за шагом. Раздел plugins
содержит плагин Gradle Error Prone для интеграции Error Prone.
В dependencies
первая строка, errorprone
загружает NullAway, а строка api
загружает библиотеку JSpecify, которая предоставляет подходящие аннотации, допускающие нулевые значения, например, org.jspecify.annotations.Nullable
. NullAway позволяет использовать любую аннотацию @Nullable
, поэтому, например, @Nullable
из библиотеки аннотаций AndroidX или аннотаций JetBrains также подойдет. Вторая строка errorprone
устанавливает используемую версию Error Prone.
Наконец, в разделе tasks.withType(JavaCompile)
мы передаем некоторые параметры конфигурации NullAway. Первая check("NullAway", CheckSeverity.ERROR)
устанавливает проблемы NullAway на уровень ошибки (это эквивалентно -Xep:NullAway:ERROR
стандартному аргументу склонности к ошибкам); по умолчанию NullAway выдает предупреждения. Затем option("NullAway:AnnotatedPackages", "com.uber")
(эквивалент стандартного аргумента -XepOpt:NullAway:AnnotatedPackages=com.uber
подверженного ошибкам) сообщает NullAway, что исходный код в пакетах в пространстве имен com.uber
должен быть проверяется на наличие нулевых разыменований и правильное использование аннотаций @Nullable
, и следует предполагать, что файлы классов в этих пакетах правильно используют @Nullable
(подробнее см. в документации). NullAway требует запуска как минимум аргумента конфигурации AnnotatedPackages
, чтобы различать аннотированный и неаннотированный код. Другие полезные параметры конфигурации см. в документации по конфигурации. Для еще более простой настройки параметров NullAway используйте плагин Gradle NullAway. Наконец, мы покажем, как при желании отключить NullAway в тестовом коде.
Мы рекомендуем устранить все проблемы, о которых сообщает Error Prone, особенно те, о которых сообщается как об ошибках (а не как о предупреждениях). Но если вы хотите опробовать NullAway без выполнения других проверок на склонность к ошибкам, вы можете использовать options.errorprone.disableAllChecks
(эквивалентно передаче компилятору "-XepDisableAllChecks"
перед аргументами, специфичными для NullAway).
Версии 3.0.0 и более поздних версий плагина Gradle Error Prone больше не поддерживают Android. Поэтому, если вы используете последнюю версию этого плагина, вам потребуется добавить дополнительные настройки для запуска Error Prone и NullAway. В нашем примере файла build.gradle
приложения показан один из способов сделать это, но ваш проект Android может потребовать внесения изменений. С другой стороны, версии 2.x плагина Gradle Error Prone Plugin по-прежнему поддерживают Android и могут работать с вашим проектом.
Кроме того, по сравнению с конфигурацией Java, зависимость JSpecify может быть удалена; Вместо этого вы можете использовать аннотацию androidx.annotation.Nullable
из библиотеки аннотаций AndroidX.
Некоторые обработчики аннотаций, такие как Dagger и AutoValue, генерируют код в том же пространстве имен пакета, что и ваш собственный код. Это может вызвать проблемы при установке NullAway уровня ERROR
, как предложено выше, поскольку ошибки в этом сгенерированном коде будут блокировать сборку. На данный момент лучшим решением этой проблемы является полное отключение Error Prone в сгенерированном коде с помощью опции -XepExcludedPaths
, добавленной в Error Prone 2.1.3 (описано здесь, используйте options.errorprone.excludedPaths=
в Gradle). Чтобы использовать, выясните, какой каталог содержит сгенерированный код, и добавьте этот каталог в регулярное выражение исключенного пути.
Примечание для пользователей Dagger : версии Dagger старше 2.12 могут плохо взаимодействовать с NullAway; см. здесь. Пожалуйста, обновите Dagger до версии 2.12, чтобы устранить проблему.
В отличие от других обработчиков аннотаций, описанных выше, Lombok модифицирует AST в памяти обрабатываемого им кода, что является источником многочисленных несовместимостей с Error Prone и, следовательно, с NullAway.
Мы не особо рекомендуем использовать NullAway с Lombok. Однако NullAway кодирует некоторые сведения об распространенных аннотациях Lombok, и мы стараемся обеспечить максимальную совместимость. В частности, должны поддерживаться такие распространенные варианты использования, как классы @lombok.Builder
и @Data
.
Чтобы NullAway успешно обнаруживал код, сгенерированный Lombok, в Java AST в памяти, в Lombok необходимо передать следующий параметр конфигурации как часть применимого файла lombok.config
:
lombok.addLombokGeneratedAnnotation = true
Это заставляет Lombok добавлять @lombok.Generated
к генерируемым методам/классам. NullAway будет игнорировать (т. е. не проверять) реализацию этого сгенерированного кода, считая его неаннотированным.
Давайте посмотрим, как работает NullAway, на простом примере кода:
static void log ( Object x ) {
System . out . println ( x . toString ());
}
static void foo () {
log ( null );
}
Этот код содержит ошибки: при вызове foo()
последующий вызов log()
завершится ошибкой с NPE. Эту ошибку можно увидеть в примере приложения NullAway, выполнив:
cp sample/src/main/java/com/uber/mylib/MyClass.java.buggy sample/src/main/java/com/uber/mylib/MyClass.java
./gradlew build
По умолчанию NullAway предполагает, что каждый параметр метода, возвращаемое значение и поле не равны нулю , т. е. ему никогда не может быть присвоено null
значение. В приведенном выше коде предполагается, что параметр x
функции log()
не равен нулю. Итак, NullAway сообщает о следующей ошибке:
warning: [NullAway] passing @Nullable parameter 'null' where @NonNull is required
log(null);
^
Мы можем исправить эту ошибку, разрешив передачу null
в log()
с помощью аннотации @Nullable
:
static void log ( @ Nullable Object x ) {
System . out . println ( x . toString ());
}
С помощью этой аннотации NullAway указывает на возможное разыменование нуля:
warning: [NullAway] dereferenced expression x is @Nullable
System.out.println(x.toString());
^
Мы можем исправить это предупреждение, добавив нулевую проверку:
static void log ( @ Nullable Object x ) {
if ( x != null ) {
System . out . println ( x . toString ());
}
}
Благодаря этому изменению все предупреждения NullAway устранены.
Более подробную информацию о проверках, сообщениях об ошибках и ограничениях NullAway можно найти в нашем подробном руководстве.
Если у вас есть какие-либо вопросы о том, как использовать NullAway, не стесняйтесь открыть вопрос на GitHub. Или вы можете присоединиться к серверу NullAway Discord и задать нам вопрос там.
Мы будем рады, если вы внесете свой вклад в NullAway! Обратите внимание: после создания запроса на включение вам будет предложено подписать наше Лицензионное соглашение Uber Contributor.
NullAway лицензируется по лицензии MIT. Дополнительную информацию см. в файле LICENSE.txt.