Как эффективно использовать @Nullable и @Nonnull аннотации?
Я вижу, что аннотации @Nullable
и @Nonnull
могут быть полезны при предотвращении NullPointerException
, но они не распространяются очень далеко.
- Эффективность этих аннотаций полностью исчезает после одного уровня косвенности, поэтому, если вы добавляете только несколько, они не распространяются очень далеко.
- Поскольку эти аннотации не соблюдаются, существует опасность того, что значение, отмеченное знаком
@Nonnull
, не является нулевым и, следовательно, не выполняет нулевые проверки.
В приведенном ниже коде параметр, отмеченный @Nonnull
, будет null
, не вызывая никаких жалоб. Он запускает NullPointerException
при запуске.
public class Clazz {
public static void main(String[] args){
Clazz clazz = new Clazz();
// this line raises a complaint with the IDE (IntelliJ 11)
clazz.directPathToA(null);
// this line does not
clazz.indirectPathToA(null);
}
public void indirectPathToA(Integer y){
directPathToA(y);
}
public void directPathToA(@Nonnull Integer x){
x.toString(); // do stuff to x
}
}
Есть ли способ сделать эти аннотации более строго принудительными и/или распространяться дальше?
Ответы
Ответ 1
Короткий ответ: я думаю, эти аннотации полезны только для вашей среды IDE, чтобы предупредить вас о возможных ошибках нулевого указателя.
Как сказано в книге "Чистый код", вы должны проверить свои параметры общедоступного метода, а также избегать проверки инвариантов.
Еще один хороший совет никогда не возвращает нулевые значения, но вместо этого использует нулевой шаблон объекта.
Ответ 2
Помимо того, что IDE дает вам подсказки о том, что вы передаете null
, где ожидается, что он не будет нулевым, вы получите следующие преимущества:
- Инструменты анализа статического кода могут тестировать то же, что и ваша IDE (например, FindBugs).
- Вы можете нам AOP проверить эти утверждения.
Таким образом, это может помочь создать код, который более удобен в обслуживании (вам не нужны проверки null
) и меньше подвержены ошибкам.
Ответ 3
Составив исходный пример в Eclipse в соответствии с 1.8 и включив нулевой анализ на основе аннотаций, мы получаем это предупреждение:
directPathToA(y);
^
Null type safety (type annotations): The expression of type 'Integer' needs unchecked conversion to conform to '@NonNull Integer'
Это предупреждение составлено по аналогии с теми предупреждениями, которые вы получаете при смешивании генерируемого кода с устаревшим кодом с использованием необработанных типов ( "непроверенное преобразование" ). Здесь мы имеем ту же самую ситуацию: метод indirectPathToA()
имеет "устаревшую" подпись, в которой он не указывает ни одного нулевого контракта. Инструменты могут легко сообщать об этом, поэтому они будут преследовать вас по всем аллеям, где нужно обменивать нулевые аннотации, но еще нет.
И при использовании умного @NonNullByDefault
мы даже не должны говорить об этом каждый раз.
Другими словами: могут ли или нет нулевые аннотации "распространяться очень далеко", зависит от используемого вами инструмента и от того, насколько строго вы следите за всеми предупреждениями, выпущенными инструментом. С TYPE_USE нулевые аннотации у вас наконец есть возможность, чтобы инструмент предупредил вас о каждом возможном NPE в вашей программе, потому что nullness становится неотъемлемым свойством системы типов.
Ответ 4
Я согласен, что аннотации "не распространяются очень далеко". Однако я вижу ошибку на стороне программиста.
Я понимаю аннотацию Nonnull
как документацию. Следующий метод выражает то, что для (необязательного условия) требуется непустой аргумент x
.
public void directPathToA(@Nonnull Integer x){
x.toString(); // do stuff to x
}
Следующий фрагмент кода затем содержит ошибку. Метод вызывает directPathToA()
, не применяя при этом, что y
не является нулевым (то есть он не гарантирует предварительное условие вызываемого метода). Одна из возможностей - добавить аннотацию Nonnull
, а также к indirectPathToA()
(распространяя предварительное условие). Вторая возможность - проверить недействительность y
в indirectPathToA()
и избежать вызова directPathToA()
, когда y
имеет значение null.
public void indirectPathToA(Integer y){
directPathToA(y);
}
Ответ 5
Я думаю, что этот оригинальный вопрос косвенно указывает на общую рекомендацию, что проверка нулевого указателя во время выполнения по-прежнему необходима, хотя используется @NonNull. См. Следующую ссылку:
Новые аннотации типов Java 8
В вышеприведенном блоге рекомендуется:
Необязательный тип Аннотации не заменяют проверку времени выполненияПеред типом аннотации основное место для описания вещей как nullability или диапазоны в javadoc. С аннотациями типа, эта связь входит в байт-код в способе компиляции проверка. Ваш код должен по-прежнему выполнять проверку выполнения.
Ответ 6
Что я делаю в своих проектах, так это активировать следующую опцию в проверке кода "Constant conditions and exceptions":
Предложить @Nullable аннотацию для методов, которые могут возвращать значения null и сообщать значения NULL, переданные в не аннотированные параметры
![Проверки]()
При активации все не аннотированные параметры будут обрабатываться как ненулевые, и вы также увидите предупреждение о своем непрямом вызове:
clazz.indirectPathToA(null);
Для еще более сильных проверок Checker Framework может быть хорошим выбором (см. этот красивый учебник.
Примечание. Я еще не использовал это, и могут возникнуть проблемы с компилятором Jack: см. this bugreport
Ответ 7
В Java я бы использовал Guava Необязательный тип. Будучи реальным типом, вы получаете гарантии компилятора о его использовании. Легко обойти его и получить NullPointerException
, но по крайней мере сигнатура метода четко сообщает, что он ожидает в качестве аргумента или того, что он может вернуть.
Ответ 8
Если вы используете Kotlin, он поддерживает эти аннотации с нулевым значением в своем компиляторе и не позволит вам передать null в java-метод, для которого требуется непустой аргумент. Событие, хотя этот вопрос был первоначально ориентирован на Java, я упоминаю эту функцию Kotlin, потому что она специально нацелена на эти аннотации Java, и вопрос был: "Есть ли способ сделать эти аннотации более строго соблюдаются и/или распространяются дальше?" и эта функция делает эти аннотации более строго соблюдаемыми.
Класс Java с помощью @NotNull
аннотации
public class MyJavaClazz {
public void foo(@NotNull String myString) {
// will result in an NPE if myString is null
myString.hashCode();
}
}
Класс Kotlin вызывает класс Java и передает значение null для аргумента, аннотированного с помощью @NotNull
class MyKotlinClazz {
fun foo() {
MyJavaClazz().foo(null)
}
}
Ошибка компилятора Kotlin, применяющая аннотацию @NotNull
.
Error:(5, 27) Kotlin: Null can not be a value of a non-null type String
см.: http://kotlinlang.org/docs/reference/java-interop.html#nullability-annotations