Значение null, nullable-типы и null-безопасность

Последнее обновление: 04.04.2023

Иногда возникает необходимость, чтобы переменная не имела никакого значения. Для этой цели в языке Dart есть специальная константа null, которая говорит, что у переменной по сути отсутствует значение. Однако при использовании этой константы следует учитывать, что в языке Dart по умолчанию применяется null-безопасность или null-safety. Возьмем, к примеру, следующую программу:

void main() {
	String name;	// нет никакого значения
	print(name);
	name = "Tom";
	print(name);
}

Здесь переменной name при ее определении не присваивается никакого значения. Однако при компиляции программы мы получим ошибку:

main.dart:3:8: Error: Non-nullable variable 'name' must be assigned before it can be used.
        print(name);
              ^^^^

Ошибка говорит нам о том, что мы пытаемся использовать переменную, которая не может принимать значение null, до присвоения ей значения.

По умолчанию все типы данных расцениваются как типы, которые не допускают значение null, то есть перед использованием должны обязательно иметь некоторое значение, например:

void main() {
	String name = "Bob";
	print(name);
	name = "Tom";
	print(name);
}

Оператор ? и nullable-типы

Однако в некоторых ситуациях может потребоваться указать, что переменная не имеет никакого значения. То есть необходимо, чтобы она могла принимать значение null. В этом случае мы можем использовать nullable-типы Они являются по сути двойниками обычных типов за тем исключением, что их переменные могут принимать значение null..

Чтобы отметить тип как nullable (допускающий значение null), после названия типа ставится оператор ? (вопросительный знак). Например:

void main() {
	String? name;
	print(name);	// null
	name = "Tom";
	print(name);	// Tom
}

Тип String? указывает, что его переменная может представлять либо строку, либо значение null. И при выполнении этой программы мы получим следующий консольный вывод:

null
Tom

Переменным таких типов также можно явным образом присваивать null:

void main() {
	String? name = "Tom";
	print(name);
	name = null;
	print(name);
}

Консольный вывод:

Tom
null

Оператор ??

Оператор ?? при присвоении переменной значений выполнит проверку на null. Зачем он нужен? Рассмотрим следующий пример:

void main() {
	int? num1 = 23;
	int num2 = num1;
	print(num2);	// 23
	
	num1 = null;
	num2 = num1;	// ! Ошибка
	print(num2);
}

Здесь определены две переменных: num1 и num2. Обе они могут хранить целые числа. Однако кроме того, переменная num1 может также хранить значение null. И переменной num2, которая НЕ может принимать null, передается значение num1, которая может хранить null. Соответственно мы можем столкнуться с потенциальной ошибкой, в случае если num1 = null. И даже компилятор не скомпилирует данную программу.

Для решения подобной проблемы можно использовать оператор ??:

value1 ?? value2

Если значение value1 (значение слева от оператора ??) не равно null, то оператор возвращает именно это значение value1. Если же это значение равно null, то оператор ?? возвращает значение value2 (справа от оператора).

void main() {
	int? num1 = 23;
	int num2 = num1 ?? 0;
	print(num2);		// 23
	
	num1 = null;
	num2 = num1 ?? 0;
	print(num2);		// 0
}

При первом присвоении переменная num2 получит значение переменной num1 (число 23), так как num1 не равна null. При втором присвоении num2 получит значение 0, так как num1 равна null.

Консольный вывод:

23
0

Иногда встречаются ситуации, когда переменной надо присвоить значение, если только она равна null. Например:

void main() {
	int? a;
	a = a ?? 10;
	print(a);		// 10
}

Здесь переменной a присваивается число 10, если эта переменная равна null. Для таких ситуаций Dart предоставляет специальный оператор - ??=:

void main() {
	int? a;
	a ??= 10;
	print(a);		// 10
}

Обычно операторы ?? и ??= применяются, чтобы гарантировать, что переменные, которые могут иметь null, будут содержать какое-то конкретное значение.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850