RequireNonNull: примеры (JAVA)
requireNonNull(T obj): TОбщее описание
Метод Objects.requireNonNull из пакета java.util проверяет ссылку на null и возвращает её же, если значение не равно null. В противном случае генерируется исключение java.lang.NullPointerException. Метод используется для явной проверки входных параметров, полей и значений, возвращаемых из других компонентов, при необходимости снабжая исключение понятным сообщением.
Сигнатуры и поведение:
- Objects.requireNonNull(T obj) - принимает один аргумент. Если obj не равен null, возвращает его, иначе выбрасывает NullPointerException без сообщения.
- Objects.requireNonNull(T obj, String message) - при null выбрасывает NullPointerException с указанным текстом. Сообщение вычисляется до вызова метода, потому его формирование выполняется всегда.
- Objects.requireNonNull(T obj, Supplier
messageSupplier) - при null вызывает messageSupplier.get() и использует возвращённую строку в качестве текста исключения. Этот вариант обеспечивает ленивое формирование сообщения; supplier не вызывается, если объект не равен null. (Supplier-версия появилась в более поздних релизах; см. раздел изменения.)
Возвращаемое значение: при ненулевом аргументе метод возвращает тот же объект и может использоваться внутри выражений и для цепочек присвоений. При равном null генерируется java.lang.NullPointerException; текст исключения зависит от используемой перегрузки.
Типичные сценарии применения: проверка аргументов в конструкторах и методах API, защита от непредвиденных null в цепочках вызовов, выражение контрактов (preconditions) и упрощение диагностики при ошибках.
Короткие примеры
Примеры показывают основные перегрузки и результаты их работы.
1) Стандартная проверка без сообщения
import java.util.Objects;
String s = null;
Objects.requireNonNull(s);
Exception in thread "main" java.lang.NullPointerException at java.util.Objects.requireNonNull(Objects.java:203) ... (stack trace)
2) Проверка с текстовым сообщением
String name = null;
Objects.requireNonNull(name, "name must not be null");
Exception in thread "main" java.lang.NullPointerException: name must not be null at java.util.Objects.requireNonNull(Objects.java:207) ... (stack trace)
3) Ленивое сообщение через Supplier
String data = null;
Objects.requireNonNull(data, () -> "missing data for id=" + computeId());
// computeId() не будет вызван, если data не null
Exception in thread "main" java.lang.NullPointerException: missing data for id=42 at java.util.Objects.requireNonNull(Objects.java:210) ... (stack trace)
4) Использование в цепочке присвоений
class User {
private final String name;
User(String name) {
this.name = Objects.requireNonNull(name, "name required");
}
}
Создание экземпляра с null приводит к NullPointerException: name required
Аналоги в Java
Существуют похожие механизмы в стандартной библиотеке и популярных библиотеках:
- Objects.requireNonNullElse и Objects.requireNonNullElseGet - возвращают альтернативное значение, если первый аргумент равен null. Удобны, когда требуется дефолт.
- java.util.Optional - для явного представления возможного отсутствия значения; предпочтителен при необходимости выразить опциональность, а не бросать исключение.
- com.google.common.base.Preconditions.checkNotNull (Guava) - функционально близок к requireNonNull, часто используется в кодовой базе, где уже применяется Guava; возвращает ссылку и при null бросает NullPointerException. В Guava сообщение формируется аналогично: есть ленивые и нежадные варианты.
Выбор зависит от контекста: если требуется немедленное падение при invalid input - requireNonNull или Preconditions.checkNotNull. Если нужно предоставить запасной объект - requireNonNullElse. Для API, где отсутствие значения - допустимая ситуация, лучше Optional.
Аналоги в других языках
Краткий обзор эквивалентов и отличий от Java-метода.
- Kotlin
val x: String? = null val y = requireNotNull(x) { "x missing" }Exception in thread "main" java.lang.IllegalArgumentException: x missing
Отличие: requireNotNull бросает IllegalArgumentException, но есть оператор !!, который генерирует NullPointerException; Kotlin лучше интегрирован с системой nullable-типов. - C#
string s = null; if (s == null) throw new ArgumentNullException(nameof(s));Unhandled Exception: System.ArgumentNullException: Value cannot be null. (Parameter 's')
Отличие: используется ArgumentNullException с именем параметра; нет стандартной функции в BCL с теми же перегрузками. - Python
s = None if s is None: raise TypeError('s must not be None')Traceback (most recent call last): ... TypeError: s must not be None
Отличие: динамическая типизация, явной стандартной утилиты нет; чаще используют проверку и raise. - JavaScript
let x = null; if (x == null) throw new TypeError('x required');Uncaught TypeError: x required
Отличие: нет встроенной requireNonNull; можно использовать операторы ?? или проверять вручную. - Go
var p *int = nil if p == nil { panic("p is nil") }panic: p is nil goroutine 1 [running]: ... (stack trace)
Отличие: в Go часто возвращают ошибку вместо panics; проверки выполняются вручную. - PHP
$x = null; if (is_null($x)) { throw new InvalidArgumentException('x required'); }Fatal error: Uncaught InvalidArgumentException: x required
Отличие: чаще выбрасывают InvalidArgumentException или TypeError. - Lua
local x = nil if x == nil then error('x required') endlua: script.lua:2: x required stack traceback: ... (stack trace)
Во многих языках проверка null/null-equivalent выполняется вручную и выбор типа исключения зависит от конвенций. В Java удобство requireNonNull в том, что метод возвращает значение и легко вложим в выражения.
Типичные ошибки
Частые проблемы при использовании и примеры.
1) Ненужное создание сообщения
Objects.requireNonNull(user, "user id=" + expensiveComputation());
expensiveComputation() выполняется всегда, даже если user не null - лишние затраты.
Решение: использовать вариант с Supplier, чтобы отложить вызов.
2) Передача null в messageSupplier
Objects.requireNonNull(obj, null); // второй аргумент равен null
Если вызвать перегрузку с Supplier и передать null в качестве supplier, при попытке получить сообщение будет NPE или поведение приведёт к NPE - передавать null вместо supplier нельзя.
3) Неправильный тип исключения для контрактов API
// ожидалось IllegalArgumentException, но используется requireNonNull
Objects.requireNonNull(param, "bad param");
В результате при нарушении контрактов вызывающий получает NullPointerException, хотя семантически более уместно ArgumentException/IllegalArgumentException.
4) Излишнее использование в местах, где null допустим
requireNonNull применяет строгую политику. Если null является валидным состоянием, проверка ломает логику. Нужно документировать контракт метода.
Изменения по версиям
- Objects.requireNonNull(T obj) и Objects.requireNonNull(T obj, String message) добавлены в ранних Java-версиях (с появлением класса Objects в JDK7).
- Перегрузка с Supplier (Objects.requireNonNull(T obj, Supplier
messageSupplier)) появилась позднее (в JDK9 и выше), чтобы обеспечить ленивое формирование сообщения и избежать лишних затрат при ненулевых значениях. - В остальном семантика метода стабилизировалась и существенных изменений в поведении не происходило.
Расширенные и нетривиальные примеры
Несколько продвинутых сценариев применения с пояснениями.
1) Ленивое сообщение с форматированием только при ошибке
String config = null;
Objects.requireNonNull(config, () -> String.format("config missing for profile=%s, host=%s", profileName(), hostName()));
// profileName() и hostName() вызываются только при null
Exception in thread "main" java.lang.NullPointerException: config missing for profile=dev, host=localhost ... (stack trace)
2) Валидация коллекции внутри stream, с выводом детального сообщения о позиции
List list = Arrays.asList("a", null, "c");
List safe = IntStream.range(0, list.size())
.mapToObj(i -> Objects.requireNonNull(list.get(i), () -> "null at index " + i))
.collect(Collectors.toList());
Exception in thread "main" java.lang.NullPointerException: null at index 1 ... (stack trace)
3) Использование в фабричном методе для безопасной инициализации
public static Connection create(String url, String user) {
Objects.requireNonNull(url, "url required");
Objects.requireNonNull(user, "user required");
return new Connection(url, user);
}
Если один из аргументов null, создание прерывается с понятным сообщением.
4) Комбинация с requireNonNullElse для резервного значения
String primary = null;
String fallback = "default";
String result = Objects.requireNonNullElse(primary, fallback);
result = "default"
5) Интеграция с API, где требуется сохранить тип и избежать unchecked-приведений
public T mustNotBeNull(T value) {
return Objects.requireNonNull(value);
}
Integer i = mustNotBeNull(5); // тип сохранён
i = 5
6) Предупреждение о побочных эффектах в supplier
Objects.requireNonNull(obj, () -> {
// избегать побочных эффектов; supplier вызывается только при ошибке
logger.error("null detected");
return "obj was null";
});
Если obj не null, логирование не произойдёт; если null, supplier выполнится.
7) Проверка полей в блоке инициализации для immutable-объекта
public final class Pair {
private final String a;
private final String b;
public Pair(String a, String b) {
this.a = Objects.requireNonNull(a, "a required");
this.b = Objects.requireNonNull(b, "b required");
}
}
// корректное использование обеспечивает, что поля никогда не равны null
При попытке создать Pair с одним из null будет выброшено соответствующее исключение с текстом.
Эти примеры демонстрируют гибкость requireNonNull: метод удобен для краткой и выразительной проверки контрактов, при этом вариации с supplier помогают оптимизировать производительность и сформировать подробные сообщения только при ошибке.