<개인공부>/[Flutter, Dart]

[Dart] 다트 기본 문법 정리 2편 (is, as)

BlockDMask 2020. 5. 16. 11:22
반응형

안녕하세요. BlockDMask 입니다.

지난 다트언어 기본문법 1편에서는 자료형, 연산자, 주석에 대해서 알아보았습니다.

이번시간에는 자료형(=타입)을 검사하는 is 키워드, 자료형을 변환하는 형변환 키워드인 as에 대해서 알아보려고 합니다.

(Dart 기본문법 1편 바로가기)

 

그럼 다트 언어 기본문법 정리 제2편 시작해보겠습니다.

 

1. 데이터 타입 검사 (is 키워드)

is, is! 키워드는 "이 타입이 이 타입이 맞느냐!" 하고 확인하는 키워드 입니다. (이 도끼가 니 도끼가 맞느냐 이런?)

is 키워드 : 같은 타입이면 true를 반환하고 다른 타입이면 false를 반환.

is! 키워드 : 같은 타입이면 false를 반환하고 다른 타입이면 true를 반환.

 

예제1. is 키워드 기본 사용법

void main() {
  //is 키워드 예제
  int a = 20;
  if (a is int) {
    print('a is int type');
  } else {
    print('a is not int type');
  }

  //is! 키워드 예제
  int b = 333;
  if (b is! double) {
    print('b is not double type');
  } else {
    print('b is double type');
  }

  //is 키워드 예제
  String str = "BlockDMask";
  if (str is int) {
    print("str is int type");
  } else {
    print("str is not int type");
  }
}

예제 결과

is 키워드 예제1번 결과

 

예제2. var 타입은 초기화된 타입으로 is 키워드가 통할까?

void main() {
  var v1 = 10;            //var를 int 타입으로 초기화
  if (v1 is int) {
    print("v1 is int type");
  } else {
    print("v1 is not int type");
  }

  var v2 = "BlockDMask";  //var를 String 타입으로 초기화
  if (v2 is String) {
    print("v2 is String type");
  } else {
    print("v2 is not String type");
  }
}

예제 결과

is 키워드 예제2번 결과

예제 결과를 보듯 var 타입으로 선언 및 초기화를 한 변수는 내부적으로 해당하는 기본 데이터 타입으로 간주하는것을 잘 확인 할 수 있습니다.

또한, if(v1 is var) 은 문장 자체가 성립하지 않습니다. is 뒤에 var 불가능!

 

 

예제3. num타입은 int, double타입으로 is 키워드가 통할까?

void main() {
  //테스트 1 : num 은 int, double 타입인가.
  print("//테스트 1 : num 은 int, double 타입인가.");
  num n1 = 100;
  if (n1 is int) {
    print("num is int type");
  } else {
    print("num is not int type");
  }

  num n2 = 99.9;
  if (n2 is double) {
    print("num is double type");
  } else {
    print("num is not double type");
  }

  //테스트 2 : int, double은 num 타입인가.
  print("\n//테스트 2 : int, double은 num 타입인가.");
  int n3 = 100;
  if (n3 is num) {
    print("int is num type");
  } else {
    print("int is not num type");
  }

  double n4 = 99.9;
  if (n4 is num) {
    print("double is num type");
  } else {
    print("double is not num type");
  }

  //테스트 3 : num은 num 타입인가.
  print("\n//테스트 3 : num은 num 타입인가.");
  num n5 = 100;
  if (n5 is num) {
    print("num is num type");
  } else {
    print("num is not num type");
  }

  num n6 = 99.9;
  if (n6 is num) {
    print("num is num type");
  } else {
    print("num is not num type");
  }
}

예제 결과

is 키워드 예제 3번 결과

예제 결과에서 보듯이 num은 하나의 타입이지만, int와 double을 포함한 타입입니다.

num => int, double이고 / int, double => num 이고 / num => num 임을 알 수 있었습니다

 

 

 

2. 데이터 타입 변환 (as 키워드)

타입변환은 이전에 공부했던 int, String, double 등 데이터 타입을 다른 타입으로 변환을 하는 것 입니다.

물론 타입마다 연관이 되어있어야 변환이 가능합니다.

더 정확히 말하면 서로 관계가 있으면 변환이 가능하고, 상위 타입으로 변환이 가능합니다.

 

또한, 형 변환은 아래와 같이 여러 문장으로 불립니다.

자료형 변환, 형 변환, 타입캐스팅, type casting, 데이터 타입변환 등.

 

as키워드를 예시로 한번 확인해 보시죠.

 

예제1. 불필요한 as 키워드 사용 (동일 타입 캐스팅)

void main() {
  int a = 33;
  int b = a as int;
      
  String str = "as keyword example";
  String copyStr = str as String;
}

예제를 살펴보면 같은 타입을 굳이 as를 이용합니다. 불필요하죠. 

dartpad.dev에서 이처럼 같은 타입을 굳이 as를 이용할때 이렇게 표기를 해줍니다.

"언네서서리한 캐스트 입니다." 라고.

아무튼 우린 예제1번처럼 저런식으로 불필요하게 as 키워드를 사용하진 맙니다.

 

 

예제2. 적절하지 못한 as 키워드 사용 (업캐스팅)

업캐스팅일때는 적절하지 못합니다.

업캐스팅 다운캐스팅을 설명하기 위해선 상속을 이해해야하는데 디테일하지 않게 한번 간단하게 이야길 해보겠습니다.

 

class Point { 
  int x; 
  int y; 
} 

class PointDetail extends Point { 
  int z; 
} 

Point라는 클래스가 존재하는데 클래스도 크게 보면 데이터 타입중에 하나라고 생각할 수 있어요. String과 같습니다.

이런걸 사용자 정의 데이터 타입이라고 하는데, 데이터 타입을 묶어서 새로운 데이터 타입을 만드는 것이라 생각해도 됩니다. (클래스, 객체 이런 키워드로 구글에 검색하면 나옵니다.)

 

아무튼 이런 Point라는 클래스를 상속 받아서, PointDetail 이라는 클래스를 만들었습니다.

상속이라는 것은 부모클래스(Point)에 있는 멤버(int x, int y 등)들을 그대로 전해주는 것을 말합니다.

자식클래스(PointDetail)에서는 부모가 물려준걸 다 받고 추가적으로 더 멤버(int z)를 만들어서 확장하여 사용하게 됩니다.

 

예를들어 에스프레소 라는 클래스에서 아메리카노 라는 클래스에게 커피를 물려주게 되면 아메리카노는 '물'이라는 멤버만 있으면 아메리카노라는 클래스가 쉽게 만들어집니다.

class 에스프레소
{
	커피콩으로 막 갈고 어떠한 작업을 걸쳐서 커피 원액이 나옴.
}

class 아메리카노					
{
	커피콩으로 막 갈고 어떠한 작업을 걸쳐서 커피 원액이 나옴. //상속 안받으면 이거 계속만들어야함.
	그 원액에 물을 탐.
}

class 아메리카노 extends 에스프레소 //상속받음
{
	그 원액에 물을 탐.
}

이런느낌 입니다 아무튼 여기는 클래스의 상속을 설명하는 곳이 아니니 이쯤 하겠습니다.

 

 

클래스를 상속 받았다는 표기는 위와 같이 합니다. 자식이 부모 삿대질 한다고 생각하면 기억 잘납니다.

다시 원점으로 돌아와서

부모클래스(Point)자식클래스(PointDetail)으로 타입 캐스팅 하는것을 다운캐스팅(down-casting)이라 하고

자식클래스(PointDetail)부모클래스(Point)로 타입 캐스팅 하는것을 업캐스팅(up-casting)이라 합니다.

 

타입 캐스팅을 할때 자식클래스를 부모클래스에 담는것은 as 키워드를 굳이 이용하지 않더라도 잘 넘어갑니다.

class Point {
  int x;
  int y;
}

class PointDetail extends Point {
  int z;
}

void main() {
  PointDetail pointDetail = PointDetail();
  pointDetail.x = 4;
  pointDetail.y = 5;
  pointDetail.z = 6;
  
  Point p = pointDetail as Point;	//굳이? 
}

 

 

예제3. 적절한 as 키워드 사용 (다운캐스팅)

위에서 설명드렸습니다. 다운캐스팅.

class Point {
  int x;
  int y;
}

class PointDetail extends Point {
  int z;
}

void main() {
  Point point = Point();
  point.x = 4; // Use the setter method for x.
  point.y = 5;
    
  PointDetail p = point as PointDetail; //다운 캐스팅
}

이렇게 부모 타입의 객체를 자식 타입의 객체로 형변환 할때 as 키워드를 사용하여 형변환 하는게 적절합니다.

하지만 저 예제도 적절한 코드는 아니죠.

 

다트 공식 문서에서는 as를 이럴때 사용하라고 나와있습니다.

Use the as operator to cast an object to a particular type if and only if you are sure that the object is of that type.

객체가 특정유형인지 확실할때 as 키워드를 이용해서 해당 객체를 형변환 하여라.

이말은 위에 배운 is 키워드를 이용해서 그 객체가 확실한지 확인하고 그 다음에 형 변환을 해라. 입니다.

다시한번 정리하면 "is로 확인하고 as 써서 형변환 해라." 입니다.

 

그럼 is 키워드를 이용해서 한번 진행해보겠습니다.

class Point {
  int x;
  int y;
}

class PointDetail extends Point {
  int z;
}

void main() {
  Point point = Point();
  point.x = 4; // Use the setter method for x.
  point.y = 5;

  if (point is PointDetail) {
    PointDetail p = point as PointDetail; //다운 캐스팅
    print("ㅇㅇ");
  } else {
    print("ㄴㄴ");
  }
}

이런식으로 하면 되겠지? 하고 코드를 실행시키면 point객체는 PointDetail 클래스를 상속 받았으니까 되겠지? 싶지만 ㄴㄴ가 나옵니다.

 

그럼 조금 예제를 적절하게 바꿔보겠습니다. 이럴때 사용하는게 적절합니다. 

말로 한번 해볼게요.

자식객체생성해서 사용하다가, 부모클래스로 받게 되었는데, 자식 객체로 다시 받아야 할때.

역시 코드로 보겠습니다.

class Point {
  int x;
  int y;
}

class PointDetail extends Point {
  int z;
}

void main() {
  PointDetail p1 = PointDetail();
  p1.x = 4; // Use the setter method for x.
  p1.y = 5;
  p1.z = 6;
  
  Point p2 = p1;  //부모타입으로 캐스팅해서 막 사용하다가
  //...
  //...
  //...
  
  //다시 point detail의 z값에 접근하거나 사용해야할때.
  if (p2 is PointDetail) {
    PointDetail p3 = p2 as PointDetail; //다운 캐스팅 (is로 판단해서 한번 확인했기 때문에 as 생략가능)
    print(p3.z);
    print("ㅇㅇ");
  } else {
    print("ㄴㄴ");
  }
}

이럴때 사용하는게 적절합니다.

as 키워드를 간단하게 설명하려 했는데, 욕심이 나서 설명이 길어졌습니다. 감사합니다.

 

ETC

참고 : https://dart.dev/guides/language/language-tour#specifying-a-library-prefix

반응형