Dart 타입 검사 연산자

세 가지 연산자 총정리(as /is/is!)

연산자기능 설명
as강제 타입 변환; 라이브러리 별칭 지정에도 사용
is타입 판단: 객체가 지정 타입에 해당하면 true 반환
is!타입 판단: 객체가 지정 타입이 아니면 true 반환

obj is T 판단 성립 조건: 객체 obj 가 타입 T 를 구현/상속해야 함(부모 클래스, 인터페이스, 널러블 타입 포함).

예를 들어 모든 객체 is Object? 는 언제나 true 입니다.

is 연산자

안전한 타입 검사

객체의 런타임 타입을 검사합니다; 결과가 true일 경우 코드 블록 내에서 수동 변환 없이 해당 타입으로 바로 사용 가능합니다. 객체가 null 이거나 타입이 일치하지 않으면 false를 반환하고 예외를 발생시키지 않습니다.

아래 예시에서 클래스 객체를 사용하는데, 클래스를 아직 배우지 않은 독자는 데이터를 생성하기 위한 틀 정도로 이해하면 됩니다.

class Person {
  String firstName = '';
}

class Employee extends Person {}

void main() {
  dynamic employee = Employee();

  // is로 타입 판단
  if (employee is Person) {
    // 분기에 진입하면 employee가 자동으로 Person 타입으로 인식됨
    employee.firstName = "Jack";
    print(employee.firstName); // Jack 출력
  }

  dynamic emptyObj = null;
  if (emptyObj is Person) {
    // null은 Person과 매치되지 않아 실행되지 않음
    print("매치 성공");
  }
}Code language: Dart (dart)

위 코드는 Person 클래스를 정의하고 해당 클래스를 상속받는 Employee 클래스를 선언했습니다.

코드 내에서 is로 객체가 Person 타입인지 확인하고 참일 경우 {} 코드 블록 안에서 바로 Person 타입으로 다룰 수 있습니다. dynamic employee = Employee(); 로 동적 타입을 사용했기 때문입니다.

두 번째 예시 dynamic emptyObj = null; 의 경우 값이 null이므로 emptyObj is Person 은 false가 되어 {} 안 코드가 실행되지 않습니다.

is! 연산자

반전 타입 검사

문법:

변수 is! 타입

사실 앞서 설명한 is 결과를 부정한 것과 동일합니다.

!(변수 is 타입) 와 등가이며, 객체가 대상 타입이 아님을 판단합니다.

예시를 살펴보겠습니다:

class Person {
  String name = '';
}

void main() {
  dynamic obj = "텍스트";
  if (obj is! Person) {
    print("obj 는 Person 타입이 아닙니다");
  }
}Code language: Dart (dart)

위 예시는 {} 블록 코드가 실행되어 “obj 는 Person 타입이 아닙니다”를 출력합니다. obj가 Person 틀로 생성된 인스턴스가 아니기 때문입니다.

as 연산자

강제 타입 변환

사용법:

(변수 as 대상타입).속성/메서드Code language: JavaScript (javascript)

해당 객체가 목표 타입임을 100% 확신할 때만 사용하세요. 그렇지 않으면 런타임 예외가 발생합니다. 프로그램 안정성 측면에서 과도한 사용은 권장하지 않습니다.

class Teacher {
  String name = '';
}

void main() {
  dynamic jason = Teacher();
  // Teacher로 강제 변환 후 속성 접근
  (jason as Teacher).name = "Jason";
  print((jason as Teacher).name); // Jason
}Code language: Dart (dart)

위 예시 출력 결과는 Jason입니다

dynamic jason = Teacher(); 로 Teacher 인스턴스를 직접 생성했으므로 확실히 Teacher 타입임을 보장할 수 있어 해당 문법 사용 시 오류가 발생하지 않습니다.

아래처럼 작성하면 오류가 발생합니다.

class Teacher {
  String name = '';
}

class Car {
  String name = '';
}

void main() {
  dynamic jason = Teacher();
  
  // Car로 강제 변환 후 속성 접근
  (jason as Car).name = "Jason";//❌오류
  print((jason as Car).name); //❌오류
}Code language: Dart (dart)

jason은 본래 Teacher 타입인데 Car로 강제 변환했기 때문에 오류가 발생합니다.

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Unhandled exception:
type 'Teacher' is not a subtype of type 'Car' in type cast
#0      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:12:10)
#1      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#2      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code language: PHP (php)

asis 핵심 비교

// 작성법1: as 강제 변환
(employee as Person).firstName = 'Bob';

// 작성법2: is로 먼저 검사 후 사용
if (employee is Person) {
  employee.firstName = 'Bob';
}Code language: JavaScript (javascript)

employeenull 이거나 Person 타입이 아닐 때 동작 차이:

  • as 방식: CastError 가 발생하고 프로그램이 중단됨;
  • is 방식: if 분기에 진입하지 않고 코드 오류 없이 아무 동작도 하지 않음.

코드 양은 조금 늘어나지만 프로그램 안정성이 높으므로 is 방식을 우선 사용하는 것을 권장합니다.

as 의 두 번째 활용법

as 의 두 번째 활용법

as 는 타입 변환 외에도 라이브러리 불러오기 시 라이브러리 별칭을 지정하는 데 사용할 수 있습니다

import 'dart:math' as math;

void main() {
  // 라이브러리 별칭으로 클래스/메서드 호출
  print(math.pi); //3.141592653589793 출력
}Code language: JavaScript (javascript)

마지막 완전 예제 코드

class Animal {}
class Cat extends Animal {}

void main() {
  dynamic obj = Cat();

  // 1. is 타입 검사
  if (obj is Animal) {
    print("obj 는 Animal 타입입니다");
  }

  // 2. is! 반전 검사
  if (obj is! String) {
    print("obj 는 문자열이 아닙니다");
  }

  // 3. as 강제 변환
  Animal a = obj as Animal;

  // 오류 예시, 실행 시 크래시 발생
  dynamic str = "hello";
  // Animal error = str as Animal; // CastError 발생
}Code language: JavaScript (javascript)

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다