포스트

[Dart] Null 안전성

Dart에서 Null 안전성과 그를 보장하기 위한 장치들을 알아보자!

Null 안전성 (Null Safety)
Null Point Exception을 프로그램 실행 전 코드를 작성하는 시점에서 점검하는 것을 뜻한다.

Null Point Exception
객체가 특정 값이 아닌 null을 가리킬 때 발생하는 오류이다.
컴파일러가 미처 걸러내지 못하고 프로그램 실행 중에 발생할 경우 치명적일 수 있다.

Dart는 Null 안전성을 지원하여 NPE 발생 가능성을 낮추었다.

이러한 Null 안전성을 지원하는 언어에서의 변수 선언은 두 가지로 구분할 수 있다.

  • Nullable
    컴파일러에 이 변수에 null을 대입할 수 있음을 명시한다.
    NPE를 고려하지 않고 Nullable 변수를 사용 시 컴파일러가 오류를 알려준다.
  • NonNull
    변수에 null을 대입할 수 없다.
    null이 대입되면 코드 작성 시 컴파일러가 오류를 알려준다.

1. Nullable 변수 선언

Nullable 변수를 선언하기 위해서는 타입 뒤에 ?를 붙이면 된다.

1
2
int? cnt1 = null;
assert(cnt1 == null);

Dart에서 모든 변수는 객체이다.

가령 Number 또한 객체이기에, Number 타입 변수도 초기값으로 null을 가질 수 있다.

그런데 변수를 선언함과 동시에 초기값을 지정하지 않을 경우 변수는 null로 초기화된다.

JavaScript에서 변수를 초기화하지 않을 경우 undefined로 초기화되는 것과 유사하다.

따라서 Non-Null 변수를 선언과 동시에 초기화하지 않으면 오류가 발생한다.

다만, 이는 전역 변수와 클래스의 멤버 변수에 한해서 적용된다.

즉, 함수에서 지역 변수를 Non-Null로 선언할 때에는 초기화할 필요가 없다.

1
2
3
4
5
6
7
8
9
10
int cnt;      // 전역에서 Non-Null을 초기화하지 않아서 ERROR

class testClass() {
  int cnt;    // 클래스 내에서 Non-Null을 초기화하지 않아서 ERROR
}

void testFun() {
  int cnt;    // 정상적인 코드
  cnt = null; // Non-Null에 null을 대입해서 ERROR
}

물론 이 경우에도 초기값을 대입하기 전에 변수를 사용하려하면 오류가 발생할 것이다.

2. var 와 dynamic 타입의 Null 안전성

2.1 var

우리는 방금 전 var로 선언한 변수의 타입을 컴파일러가 추론한다는 것을 배웠다.

이때 var변수의 null 허용 여부 또한 대입 값에 따라 타입과 함께 결정된다.

따라서 var 뒤에는 ?를 붙일 수 없다.

2.2 dynamic

dynamic 타입은 모든 타입의 데이터를 사용할 수 있기에, 독자들은 이렇게 생각할 것이다.

? 붙여봤자 의미가 없는데 그럼 var처럼 ?를 못붙이지 않을까?

모든 타입의 데이터에는 Nullable 또한 포함될 것이므로 이러한 추론은 합리적인 것으로 보인다.

그런데 이게 뭐람?

1
2
3
4
5
6
7
8
9
dynamic x = 10;
dynamic y;
dynamic? z;

void testFun() {
  x = null;
  y = null;
  z = null;
}

dynamic의 경우 ?를 뒤에 붙여도 상관이 없다!

물론 그걸 붙인다고 의미는 없다

3. Non-Null 변수의 초기화 미루기

그런데 변수를 선언함과 동시에 초기값을 지정하지 않을 경우 변수는 null로 초기화된다.
따라서 Non-Null 변수를 선언과 동시에 초기화하지 않으면 오류가 발생한다.

앞서 우리는 Non-Null 변수의 경우 선언과 함께 초기화를 해야 함을 배웠다.

그런데 변수를 null인 상태로 이용하다 필요할 때에 값을 결정해야 하는 경우는 어떻게 할까?

이럴 때 late 연산자를 쓰면 된다.

당신을 걱정하는 컴퓨터에게 “알빠노?” 를 박아보자.

1
2
3
4
5
6
7
8
int x;                // 전역에서 Non-Null을 초기화하지 않아서 ERROR
late int y;           // 정상적인 코드

void main() {
  print("${y + 1}");  // 변수를 사용하기 전에 초기화하지 않아서 ERROR
  y = 1;
  print("${y + 1}");  // 정상적인 코드
}

Reference

Dart Document

이 게시물은 저작권자의 CC BY 4.0 라이센스를 따릅니다.