개발을 할 때 중요하지만 자주 신경쓰지 못하는 것 중 하나가 바로 null safety이다.
먼저 non-nullable type과 nullable type이 무엇인지 알아보자.
Non-nullable type이란 말 그대로 null 값을 가질 수 없는 타입을 의미한다.
대표적으로 String이 있는데, String 타입은 항상 문자열을 가지고 있어야 한다.
다음 코드는 int 타입을 선언했지만 null 값을 가질 수 없기에 오류가 발생한다.
void main() {
int a;
a = null;
print('a is $a.');
}
Nullable type은 반대로 null 값을 가질 수 있는 타입을 의미한다.
Null 값을 가질 수 있음을 나타내는 것은 생각보다 간단한데, 바로 타입 바로 뒤에 물음표 (?)를 붙여주는 것이다.
다음 코드는 int? 타입을 선언했기에 null 값을 가질 수 있어 오류가 발생하지 않는다.
void main() {
int? a;
a = null;
print('a is $a.');
}
generic 타입에 대해서도 동일한 방법으로 nullalbe type을 정의할 수 있다.
다음 코드는 List 자체가 null이기 때문에 List?으로 정의하면 오류가 발생하지 않는다.
void main() {
List<String>? aNullableListOfStrings;
print('aNullableListOfStrings is $aNullableListOfStrings.');
}
다음 코드는 List 안에 있는 String 타입 일부가 null이기 때문에 List<String?>으로 정의하면 오류가 발생하지 않는다.
void main() {
List<String?> aListOfNullableStrings = ['one', null, 'three'];
print('aListOfNullableStrings is $aListOfNullableStrings.');
}
nullable한 타입이지만 null 값이 절대 들어가지 않을 것을 안다면, null assertion operator를 이용하여 non-nullable하다고 알려줄 수 있다.
이 방법 또한 간단한데, 뒤에 느낌표 (!)만 붙이면 된다.
다음 코드는 nullable한 타입으로 선언되었지만 null 값이 오지 않을 것이므로 !를 사용했다.
int? couldReturnNullButDoesnt() => -3;
void main() {
int? couldBeNullButIsnt = 1;
List<int?> listThatCouldHoldNulls = [2, null, 4];
int a = couldBeNullButIsnt;
int b = listThatCouldHoldNulls.first!; // first item in the list
int c = couldReturnNullButDoesnt()!.abs(); // absolute value
print('a is $a.');
print('b is $b.');
print('c is $c.');
}
앞에서 어떤 값이 null이 아니라는 것을 알면 ! operator로 이를 표시할 수 있었다.
그러나 그럼에도 null이 오면 예외가 발생하므로 conditional property access operator와 null-coalescing operator 중 하나를 사용할 수 있다.
nullable한 타입이 진짜 null인지 아닌지 확실하지 않다면, conditional property access를 사용할 수 있다. 이는 뒤에 ?. 을 붙여주면 된다.
다음은 nullableString 값이 null인지 알 수 없으므로 ?. 을 붙여 length에 접근했다.
int? stringLength(String? nullableString) {
return nullableString?.length;
}
nullable한 타입이 진짜 null인 경우, null-coalescing operator을 이용하여 대신 보여줄 다른 값을 지정할 수 있다.
이는 ?? 을 붙여준 뒤, 그 다음에 대신 보여줄 값을 넣으면 된다.
다음은 그 예시이다.
print(nullableString ?? 'alternate');
late 키워드는 어떤 변수가 non-nullable해야하는데, 당장 값이 할당되지 않는 변수에게 사용된다.
이 키워드를 사용하면, 값을 나중에 할당할 것이고 해당 변수가 사용되기 전에 값을 무조건 가진다는 의미이다.
따라서 late 키워드와 함께 변수를 선언하면 에러가 throw된다.
다음은 late 키워드를 이용한 예시이다.
class Meal {
late String _description;
set description(String desc) {
_description = 'Meal description: $desc';
}
String get description => _description;
}
void main() {
final myMeal = Meal();
myMeal.description = 'Feijoada!';
print(myMeal.description);
}