Avatar ·

Какую Java-аннотацию @NonNull/@NotNull лучше использовать? Сравнение и рекомендации

Всем привет! Я начинающий Java-разработчик, и уже второй день ломаю голову над, казалось бы, простым вопросом: какую аннотацию @NotNull (или @NonNull) использовать в проекте?

Проблема в том, что я вижу их в разных местах и не понимаю, какая из них "правильная". Например, в IntelliJ IDEA есть своя org.jetbrains.annotations.NotNull. В проекте Spring Boot я наткнулся на javax.annotation.Nonnull (теперь jakarta.annotation.Nonnull). А коллега сказал, что лучше использовать jakarta.validation.constraints.NotNull или lombok.NonNull.

Вот простой пример, который меня смущает:

import org.jetbrains.annotations.NotNull;

public class UserService {

    public String greet(@NotNull String name) {
        return "Hello, " + name;
    }

    public static void main(String[] args) {
        UserService service = new UserService();
        System.out.println(service.greet(null)); // Ничего не упадёт во время компиляции!
    }
}

Я ожидал, что аннотация @NotNull заставит код упасть с ошибкой, если я передаю null. Но программа спокойно скомпилировалась и запустилась, получив NullPointerException уже в рантайме. Получается, что эти аннотации ничего не проверяют? Тогда зачем они нужны?

Ещё заметил, что в разных библиотеках аннотации называются по-разному: @NonNull, @NotNull, @Nonnull. Это одно и то же? Или есть разница? Какую из них выбрать для нового проекта, чтобы она работала и с IDE, и с линтерами, и с фреймворками?

Буду благодарен за разъяснение с примерами!

<<>>

Краткий ответ: используйте org.jetbrains.annotations.NotNull (или @Nullable) для статического анализа, а для валидации в рантайме — jakarta.validation.constraints.NotNull

Вы совершенно правы: аннотации @NotNull сами по себе не генерируют код проверки. Это метаданные. Они нужны для:

  • Статического анализа кода — чтобы IDE (IntelliJ IDEA, Eclipse) или инструменты вроде SpotBugs, Error Prone предупреждали вас о потенциальных NPE ещё до запуска программы.
  • Документирования контракта — чтобы другой разработчик (или вы через месяц) сразу понимал, что параметр не должен быть null.
  • Bean Validation (JSR 380) — для автоматической проверки на уровне фреймворка, например, в Spring MVC или JPA.

Почему ваш код не упал при компиляции?

Потому что аннотации @NotNull — это не встроенная фича языка Java. Это просто маркеры в байт-коде. Чтобы они работали как проверки, нужно:

  1. Настроить IDE на их анализ (обычно включено по умолчанию в IntelliJ).
  2. Использовать плагин для сборки, который проверяет аннотации на этапе компиляции (например, jsr305 + checkerframework).

В вашем примере IntelliJ IDEA подсветит вызов service.greet(null) жёлтым предупреждением, но не заблокирует компиляцию. Чтобы сделать это ошибкой, нужно включить инспекцию "Constant conditions & exceptions" на уровне "Error".

Сравнение популярных аннотаций

АннотацияПакетНазначениеКогда использовать
@NotNull org.jetbrains.annotations Статический анализ (IntelliJ, Kotlin) Для проектов, где вся команда использует IntelliJ IDEA
@Nonnull jakarta.annotation (ранее javax.annotation) JSR 305 — общий стандарт для статического анализа Для кроссплатформенных библиотек, поддержки FindBugs/SpotBugs
@NotNull jakarta.validation.constraints (ранее javax.validation) Bean Validation (JSR 380) — проверка в рантайме Для DTO, REST-запросов, Entity — где нужна runtime-валидация
@NonNull lombok Генерация кода (Lombok) Для автоматической вставки Objects.requireNonNull() в сгенерированные методы

Рекомендация: комбинируйте подходы

Для нового проекта я рекомендую следующую стратегию:

  • Для статического анализа — используйте org.jetbrains.annotations.NotNull (если у вас IntelliJ) или jakarta.annotation.Nonnull для универсальности. Подключите Checker Framework или SpotBugs в Maven/Gradle.
  • Для рантайм-валидации — добавьте зависимость jakarta.validation (Hibernate Validator) и используйте jakarta.validation.constraints.NotNull на полях DTO и параметрах REST-контроллеров.
  • Для явной проверки в коде — используйте Objects.requireNonNull(param, "param must not be null"); в начале методов. Это гарантирует ранний выброс NPE с понятным сообщением.
import org.jetbrains.annotations.NotNull;
import java.util.Objects;

public class UserService {

    public String greet(@NotNull String name) {
        // Явная проверка с понятным сообщением
        Objects.requireNonNull(name, "Имя не должно быть null");

        // Если name = null, упадёт здесь, а не при конкатенации строк
        return "Hello, " + name;
    }
}

Итог

  • @NotNull — это не магия, а контракт.
  • Для защиты от NPE на этапе компиляции — настройте статический анализатор.
  • Для защиты в рантайме — используйте Objects.requireNonNull() или jakarta.validation.
  • Не смешивайте аннотации из разных пакетов без необходимости — это запутывает.
<<>>

Альтернативный подход: использовать модуль java.util.Objects и Optional

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

В современной Java (8+) можно вообще обойтись без сторонних аннотаций, используя встроенные средства:

import java.util.Objects;
import java.util.Optional;

public class UserService {

    // Вариант 1: requireNonNull с сообщением (бросает NPE)
    public String greet(String name) {
        Objects.requireNonNull(name, "name must not be null");
        return "Hello, " + name;
    }

    // Вариант 2: Optional как контракт (явно показывает, что null возможен)
    public String greetOptional(Optional<String> name) {
        return "Hello, " + name.orElse("Guest");
    }
}

Плюсы этого подхода:

  • Нет зависимостей от сторонних библиотек (кроме стандартной библиотеки Java).
  • Код самодокументируемый: Objects.requireNonNull сразу видно.
  • Optional явно говорит, что значение может отсутствовать.

Минусы:

  • Нет статического анализа от IDE — вы не увидите предупреждение до запуска.
  • Придётся писать проверки вручную (но это не так страшно, как кажется).

Если ваш проект небольшой или вы только учитесь — начните с Objects.requireNonNull(). Это научит вас явно обрабатывать null, а не полагаться на "магические" аннотации. Аннотации добавьте позже, когда поймёте, что они реально нуж