본문 바로가기
Swift

타입 캐스팅에 관하여

by iOS 개린이 2023. 3. 13.

타입 캐스팅

-Swift의 타입 캐스팅은 인스턴스의 타입을 확인하거나, 해당 인스턴스를 슈퍼 클래스나, 하위 클래스인 것처럼 취급하는 방법이다.

-is와 as 연산자를 통해 구현하며, 두 연산자를 통해 값의 타입을 확인하거나 다른 타입으로 전환할 수 있다.

또한 타입이 프로토콜을 준수하고 있는지 여부도 확인할 수 있다.

 

 

Latte와 Americano 클래스에서 Coffee를 상속 받고 있다.

이 관계의 포함도를 그려보면 ?

 

그림을 보면 Coffee 클래스가 갖는 특성들을 Latte나 Americano가 모두 포함하고 있지만, 

Coffee는 Latte나 Americano의 특성들을 이용할 수 없다.

다시 말하면 Coffee는 Latte나 Americano인 것처럼 할 수 없지만, 반대로 Latte나 Americano는 Coffee인 척을 할 수 있다.

이 포함도를 이해하는 것이 타입캐스팅을 이해하는 시작점이다.

 

 

 

is - 데이터 타입 확인

-타입 확인 연산자로, 인스턴스가 어떤 클래스의 인스턴스인지 타입을 확인해볼 수 있다.

-인스턴스가 해당 클래스의 인스턴스거나 그 자식 클래스의 인스턴스라면 true를 반환하고, 아니면 false를 반환한다.

-런타임 시점에 실제 체크가 이루어진다.

-클래스의 인스턴스뿐만 아니라 모든 데이터 타입에 사용할 수 있다.

 

is의 사용

 

Coffee, Latte, Americano 클래스의 인스턴스를 모두 만들어주고, 각 타입 캐스팅을 실행해보았다.

 

1. Coffee의 인스턴스 coffee는 Coffee 타입이니 당연하게 true를 반환한다.

2. 포함도에서 보았듯이 Coffee 클래스는 Latte의 기능을 포함하고 있지 않기 때문에 false를 반환한다.

3. 동일하게 Coffee 클래스는 Americano의 기능을 포함하고 있지 않기 때문에 false를 반환한다.

4. Coffee 클래스를 상속받고 있는 Latte의 인스턴스인 myCoffee는 Coffee의 특성을 포함하고 있기 때문에 true를 반환해준다.

5. 하지만 myCoffee(Latte)와 Americano는 서로 특성이 다르고, 부모와 자식 클래스의 관계가 아니기 때문에 false를 반환한다.

 

 

 

as - 타입 캐스팅

-타입 캐스팅은 실제로 인스턴스를 수정하거나 값을 변경하는 작업이 아니다. (인스턴스는 메모리에 똑같이 남아있음)

단지 인스턴스를 사용할 때 어떤 타입으로 다루고 어떤 타입으로 접근해야 할 지 판단할 수 있도록 컴퓨터에게 힌트를 주는 것이다.

 

 

다운 캐스팅

-특정 클래스 타입의 상수 또는 변수는 하위 클래스의 인스턴스를 참조할 수 있다.

이 경우에 타입 캐스팅 연산자 as를 이용해서 서브 클래스 타입으로 다운캐스팅을 해준다.

 

 

myCoffee 상수는 Coffee 인스턴스를 참조하도록 선언했지만, 실제로는 Coffee 타입 행세를 하는 Latte 타입의 인스턴스를 참조하고 있다. 

여기서 만약 myCoffee가 진짜 Latte 타입으로 사용해야 할 경우가 주어진다면?

Latte 타입에 정의되어 있는 메서드나 프로퍼티에 접근해야 한다면 Latte 타입으로 변환시켜주어야 하는데, 이를 다운 캐스팅이라고 한다.

 

Latte는 Coffee를 상속받고 있기 때문에 Coffee는 부모클래스이고, Latte는 자식클래스죠?

Coffee(부모)를 Latte(자식)로 변환해주는 것이기 때문에 다운 캐스팅이라고 부르는 것이다.

 

다운 캐스팅은 실패할 수 있기 때문에, 타입 캐스팅 연산자가 두 가지 형태로 제공된다.

as? : 조건부 연산자인 ? 연산자는 옵셔널 값을 반환하는 것으로 다운 캐스팅이 실패했을 경우 nil을 반환한다.

as! : ! 연산자는 강제 언래핑한 값을 반환해주는 것으로 as!를 사용했을 때 다운 캐스팅에 실패한다면 런타임 오류가 발생한다.

 

따라서 다운 캐스팅이 무조건 성공한다는 확신이 들 때만 as! 를 사용해야 한다.

 

다운 캐스팅 사용

 

Coffee 타입의 배열 안에 Latte와 Americano 타입의 요소들을 넣어주었다.

Latte와 Americano는 Coffee 행세가 가능하기 때문에 둘 다 같은 배열에 들어갈 수 있는 것이다.

 

as?는 옵셔널 값을 반환해주기 때문에 if let 을 통해 값을 꺼내준다. (예제에서는 해당 값에 접근하는 코드가 없어서 안 써줘두댐)

 

for문을 통해 해당 요소가 Latte로 다운캐스팅이 가능하면 "Latte Down Casting"을 실행하고,

해당 요소가 Americano로 다운캐스팅이 가능하면 "americano Down Casting"을 실행해준다.

 

 

결과는?

 

Latte와 Americano는 Coffee(부모 클래스)의 자식 클래스이기 때문에 다운캐스팅이 가능한 것이다.

 

 

업 캐스팅

-서브 클래스 인스턴스를 슈퍼 클래스의 타입으로 참조한다.

-업 캐스팅은 다운캐스팅과 다르게 항상 성공한다.

 

-다운 캐스팅의 사용예제에서

let array : [Coffee] = [Latte(), Latte(), Americano()] 이 코드를 볼 수 있었다.

타입에 민감한 Swift에서 이 배열이 가능한 이유는

"Latte와 Americano는 Coffee 행세가 가능하기 때문에" 이다.

이를 기술적으로 설명해보면, Latte와 Americano는 슈퍼 클래스가 Coffee로 동일하기 때문에 Latte와 Americano를 슈퍼 클래스로 업캐스팅해서 묶었기 때문에 가능한 것이다.

 

-as 연산자를 이용해서 업캐스팅을 해줄 수도 있다.

as연산자는 컴파일 단계에서 캐스팅 가능 여부를 결정한다.

 

 

업캐스팅의 사용

 

myCoffee처럼 as를 이용해서 업캐스팅이 가능하고, 

yourCoffee처럼 Type Annotation을 사용한 업캐스팅도 가능하다!

 

 

 

as들의 실행 시점

as : 컴파일 타임에 실행된다.

as? : 런타임에 실행된다.

as!  : 런타임에 실행된다.

 

 

 

 

Any & AnyObject의 타입 캐스팅

-특정 타입을 지정하지 않고, 여러 타입의 값을 할당할 수 있는 특별한 타입이다.

 

-타입 캐스팅을 사용할 때, 보통 상속 관계에 있는 클래스끼리 캐스팅이 가능하다.

하지만 Any와 AnyObject 타입을 사용할 경우에는 상속 관계가 아니여도 타입 캐스팅이 가능하다.

 

-먼저 Any와 AnyObject에 대해서 알아보자.

(Any와 AnyObject의 사용은 꼭 필요한 상황이 아니라면 사용을 지양하는 것이 좋다.

예기치 못한 오류가 발생할 확률이 높아지므로 코드에서 처리할 타입은 항상 구체적으로 기술하는 것이 좋다.)

 

Any

-함수 타입을 포함한 모든 타입을 저장할 수 있는 타입이다.

ex) Any

 

Any를 사용하면 위의 코드처럼 모든 타입의 인스턴스를 나타낼 수 있음.

 

AnyObject

-모든 클래스 타입의 인스턴스를 나타낼 수 있다.

-타입이 AnyObject로 선언될 경우, 클래스 타입만 저장이 가능하다.

ex) AnyObject

 

Any의 예제처럼 Int, Doble, String 등 이것저것 다 넣으면 모두 에러발생.

Coffee나 Latte와 같은 클래스 타입만 저장이 가능하다.

 

 

Any와 AnyObject의 타입 캐스팅

 

1. as를 이용한 패턴매칭

 

-as는 업캐스팅의 용도 외에도 패턴매칭에 쓰일 수 있다.

 

위의 Any의 예제에서 이어진 코드를 보자.

 

Any 배열에 들어가 있는 요소들이 switch문안에서 해당 타입(캐스팅 성공)일 경우 해당 case문이 실행되는 것이다.

 

 

2. 다운캐스팅

 

 

위의 코드는 Any타입으로 선언했는데 값을 보면 String타입이다.

 

그래서 .을 통해 String 관련 메서드나 프로퍼티에 접근하려고 하면?

 

아무것도 안띄어주고, 억지로 프로퍼티나 메서드를 사용하려고 해도 에러가 발생한다.

 

이유는?

Any나 AnyObject는 무엇이든 가능한 타입이기 때문에 타입 결정이 런타임 시점에 이루어진다.

따라서 컴파일 시점에는 값이 String이든, Int든 해당 타입이 어떤 타입인지 알 수가 없다.

 

만약 Any로 타입선언 후 String으로 사용하고 싶다면 as를 이용한 다운캐스팅을 이용하자.

 

 

 

 

 

 

 

 

Reference:

-야곰님의 스위프트 문법 개정3판

-Swift ) Type Casting (tistory.com)

-[Swift] Type Casting (is, as, as?, as!) 타입캐스팅 완벽 정리 (tistory.com)

-Swift) Any와 AnyObject 알아보기 (tistory.com)

'Swift' 카테고리의 다른 글

패턴에 관하여  (0) 2023.03.15
Nested Types(중첩 타입)에 관하여  (0) 2023.03.13
모나드에 관하여  (0) 2023.03.10
고차함수에 관하여  (0) 2023.03.06
Closer에 관하여2  (0) 2023.03.02