티스토리 뷰

Mobile/Dart

[Dart] Null Safety

춘햄 2022. 6. 27. 23:22

Dart는 컴파일 과정에서 Null Point Exception을 잡아서 런타임 에러가 덜 발생할 수 있도록 해주는 개념이다.

 

즉, 변수 자체가 선언 과정에서 초기화되지 않거나, null 값을 가지면 컴파일러가 바로 에러를 잡아낼 수 있다.

 

Dart에서 처음 접해보는 개념이지만, 그렇게 어렵지도 않고 사용하기는 편리하니 바로 한번 확인을 해보자. 

 

* 이번 포스팅은 null safety에 대하여 아주 자세하게 정리한 블로그에서 참고하였다.


1. Nullable과 Non-Nullable

 

 Null Safety 가 지원이 되려면 가장 기본적으로 변수 선언 시 Nullable과 Non-Nullable로 구분되어 선언해 주어야 한다.

Dart 언어의 변수는 기본이 Non-Nullable 로 선언되는 것이며 만악 Nullable로 선언하고자 한다면 타입명 뒤에? 를 추가해 주어야 한다.

int a1=10;
int? a2=10;

a1 은 int 로 선언되어 있으며 a2는 int?로 선언되어 있다.

모두 정수 데이터를 저장하기 위한 타입으로 선언된건 맞지만? 가 있고 없고의 차이는 Null Safety에서 큰 차이가 있다.

 

int로 선언되면 이 변수에는 null 대입이 불가능한 Non-Nullable로 선언된 것이며 int?로 선언하면 이 변수에는 null 대입이 가능한 Nullable로 선언된 것이다.

int a1=10;

int? a2=10;



testFun() {

  a1=null;//error

  a2=null;

}

2. Null Safety 규칙

 

Null Safety 개념이 이해 되었다면 이제 몇 가지 규칙을 추가 정리해 보자.

 1) Non-Nullable 변수는 선언과 동시에 초기값을 주어야 한다.

int a1;//error

int? a2;

 


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

 

그런데 변수를 선언하면서 초기값을 주지 않으면 null로 자동 초기화된다. 

 

하지만 Non-Nullable 로 선언된 변수는 null로 자동 초기화된다는 것은 말이 안 되기 때문에, Non-Nullable 선언된 변수는 선언과 동시에 명시적으로 초기값을 주어야 한다.

 

 2) var 타입에서의 Null Safety

 

 Dart 에서 변수 선언 시 타입을 var로 선언하여 타입을 유추하도록 할 수 있다. 
즉, var 로 타입 선언 시 Nullable과 Non-Nullable도 자동 유추가 되기 때문에 var 타입 뒤에는? 를 추가할 수 없다.

var a1=10;

var a2=null;

var a3;

var? a4=null;//error

 

3) dynamic 타입에서의 Null Safety

 

 dynamic 타입에는 ? 가 추가되어 선언될 수 있지만 의미가 없다. 


dynamic 타입이 대입되는 값을 한정하지 않겠다는 의미이며 모든 타입의 데이터가 대입될 수 있다는 의미이다. 
여기에는 Nullable도 포함이 되기 때문에 dynamic 타입으로 선언되는 것 자체가 Nullable 로 선언되는 것이다.

dynamic a1 = 10;

dynamic a2;

dynamic? a3;



testFun() {

  a1=null;

  a2=null;

  a3=null;

}

 

4) Local Variable 에서만 Non-Nullable 변수가 선언과 동시에 초기화되지 않아도 된다.

 

 ? 가 추가되지 않아 Non-Nullable 로 선언된 변수는 null 대입이 불가함으로 선언과 동시에 초기값을 주어야 한다. 


하지만 이는 Top-Level 에 선언된 변수와 클래스 멤버 변수의 규칙이며 함수 내에 선언되는 Local Variable 은 변수가 Non-Nullable로 선언되었다고 하더라도 선언과 동시에 초기값을 주지 않아도 된다.

 

testFun() {

  int a1;

  a1=10;

  print(a1+10);//ok

}

5) casting Test

 

Nullable 로 선언된 변수가 Non-Nullable로 선언된 변수에 대입이 가능한지, 반대로 Non-Nullable로 선언된 변수가 Nullable로 선언된 변수에 대입이 가능한지를 확인해보면, 타입적으로 Nullable 변수는 Non-Nullable 변수의 상위 타입이기 때문에 int? 타입이 int 타입의 상위 타입이 된다. 


그럼으로 Non-Nullable 변수가 Nullable에 대입되는 것은 스마트 캐스팅에 의해 자동으로 형 변형이 된다. 


하지만 Nullable 변수가 Non-Nullable 에 대입되는 것은 에러이며 하고자 한다면 명시적 캐스팅을 사용해 주어야 한다.

int a1=10;

int? a2=10;



main() {

  a1=a2;//error

  a2=a1;//ok

}

a1 은 Non-Nullable 로 선언되어 있으며 a2는 Nullable로 선언되어 있다.


a1 이 a2 에 대입되는 데는 문제가 없지만 a2 가 a1에 대입되면 에러가 발생하게 되며 가능하게 하려면 아래의 코드처럼 명시적 캐스팅을 해주어야 한다.

int a1=10;

int? a2=20;



main() {

  a1=a2 as int;//ok

  print("a1: $a1, a2: $a2");//a1: 20, a2: 20

}

* Dart 에서 명시적 캐스팅 연산자는 as이다.


3. Null Safety Operator

 

 1)!: null check operator — runtime error throw

 

 어떤 변수 뒤에! 을 추가하면 이 변수 값이 null 인 경우 runtime error 발생하게 된다.

int? a2=20;
 
 main() {
   a2!;
   a2=null;
   a2!;
 }

위의 코드를 실행시켜 보면 마지막 라인에서 런타임 에러가 발생하게 된다.

 

! operator 은 nullable variable을 non-nullable에 대입할 때도 사용이 가능하다.

int a1=10;
int? a2=20;
 
 main() {
   // a1=a2;//compile error
   a1 = a2 as int;//ok
 
   a1=a2!;
   print('step 1 : a1 = $a1');//step 1 : a1 = 20
 
   a2=null;
   a1=a2!;//runtime error
   print('step 2 : a1 = $a1');
 }

 2) late keyword

 

 non-nullable로 변수가 선언되면 선언과 동시에 초기값을 주어야 한다. 
그런데 어떤 경우에는 변수가 non-nullable로 선언되기는 하지만 선언과 동시에 초기값을 줄 수 없는 경우도 있다. 


즉, 변수가 null 인 상태로 이용되지는 않지만 초기값이 앱이 실행되면서 결정되는 경우이다. 

이때 late keyword를 사용하면 된다. 


late는 단어 뜻 그대로 초기화 시점을 뒤로 미루겠다는 의미이다.

int a1;//compile error
late int a2;//ok

 

'Mobile > Dart' 카테고리의 다른 글

[Dart] 비동기 프로그래밍: future, async, await  (0) 2022.06.27
[Dart] factory  (0) 2022.06.26
[Dart] Extends, Implements, With  (0) 2022.06.26
[Dart] Dart 기본  (0) 2022.06.26
Comments