[Swift 문법] 타입캐스팅

!·2022년 9월 3일
0

Swift 문법

목록 보기
26/27

타입캐스팅

다른 프로그래밍 언어(C, Java 등등..) 에서는 암묵적 타입 캐스팅 을 지원한다.
하지만 스위프트는 암묵적 타입 캐스팅을 지원하지 않을 뿐더러, 다른 타입간의 변환을 매우 엄격하게 제한하기 때문에 스위프트에서 타입캐스팅 은 조금 다른 의미로 쓰인다.
우리가 다른 프로그래밍에서 쓰는 명시적 형변환은 스위프트에서는 이니셜라이저(String(abc))로 구혀하기때문에 이를 타입캐스팅이라고 하기에는 무리가 있다.


데이터 타입 확인

스위프트의 타입캐스팅은 인스턴스의 타입을 확인하거나, 자신을 다른 타입의 인스턴스인 양 행세할 수 있도록 해준다.

class Coffee{
    let name: String
    let shot: Int
    
    var description: String{
        return "\(shot) shot(s) \(name)"
    }
    
    init(shot: Int){
        self.shot = shot
        self.name = "coffee"
    }
}

class Latte: Coffee{
    var flavor: String
    
    override var description: String{
        return "\(shot) shots \(flavor) latte"
    }
    
    init(flavor: String, shot: Int){
        self.flavor = flavor
        super.init(shot: shot)
    }
}

class Americano: Coffee{
    let iced: Bool
    
    override var description: String{
        return "\(shot) shot(s) \(iced ? "iced" : "hot") americano"
    }
    
    init(shot: Int, iced: Bool){
        self.iced = iced
        super.init(shot: shot)
    }
}

상위클래스의 coffee 인스턴스는 Americano 클래스와 Latte 클래스의 인스턴스라고 할 수는 없지만 반대의 경우엔는 가능하다. 이는 is 연산자를 통해 확인 가능하다.

let coffee: Coffee = Coffee(shot:  1)
let myCoffee: Americano = Americano(shot: 2, iced: false)
let yourCoffee: Latte = Latte(flavor: "green", shot: 3)

coffee is Coffee		//	true
coffee is Americano		//	false
coffee is Latte			//	false
	
myCoffee is Coffee		//	true
yourCoffee is Coffee	//	true

myCoffee is Latte		//	false
yourCoffee is Latte		//	true

메타 타입 타입

메타 타입 타입 은 타입의 타입을 뜻한다. 클래스 타입, 구조체 타입, 열거형 타입, 프로토콜 타입 등의 타입의 타입이다. 즉, 타입 자체가 하나의 타입으로 또 표현할 수 있다는 것이다.
타입의 이름 뒤에 .Type 를 붙이면, 이는 메타 타입을 나타낸다.

protocol SomeProtocol() {}
class SomeClass: SomeProtocol {}

let intType: Int.Type = Int.self
let stringType: String.Type = String.self
let classType: SomeClass.Type = SomeClass.self
let protocolType: SomeProtocol.Protocol = someProtocol.self

var someType: Any.Type
someType = intType
someType = stringType

self

  • .self 표혀은 값 뒤에 써주면 그 값 자신을, 타입 뒤에 써주면 그 타입을 표현하는 값을 반환한다.
    "stringValue".self 는 "stringValue" 를 반환하며, String.self는 String 타입을 나타내는 값을 반환한다.
    만약 프로그램 실행 중에 인스턴스의 타입을 표현한 값을 알아보고자 한다면 type(of: ) 함수를 사용한다. 그래서 type(of: instance).self 라고 표현하면 someInstance의 타입을 값으로 표현한 값을 반환한다.
type(of: coffee) == Coffee.self				// true
type(of: coffee) == Americano.self			// false
type(of: coffee) == Latte.self				// false
 
type(of: coffee) == Americano.self			// false
type(of: myCoffee) == Americano.self		// true
type(of: yourCoffee) == Americano.self		// false

type(of: coffee) == Latte.self				// false
type(of: myCoffee) == Latte.self			// false
type(of: yourCoffee) == Latte.self			// true
	
type(of: myCoffee) == Coffee.self			// false

다운 캐스팅

어떤 클래스 타입의 변수 또는 상수가 정말로 해당 클래스의 인스턴스를 참조하지 않을 수도 있다. 예를 들어 Latte 클래스의 인스턴스가 Coffee 클래스의 인스턴스인양 Coffee 행세를 할 수 있다.

let actingConstant: Coffee = Latte(flavor: "vanila", shot: 2)
print(actingConstant.description) // 2 shot(s) vanila latte

타입캐스트 연산자에는 as?as! 두 가지가 있다. as? 같은 경우에는 다운 캐스팅시 실패할 경우 nil 을 반환하며, 성공할 경우 옵셔널로 반환해준다. as! 같은 경우에는 실패할 경우 런타임에러가 발생하며, 성공할 경우 옵셔널이 아닌 인스턴스를 반환해준다.

let actingOne: Americano = coffee as? Americano // 실패
let castedAmericano: Americano = coffee as! Americano // 실패

actingOne의 경우 만약 coffee가 가리키는 인스턴스가 Americano 타입의 인스턴스라면 actingOne 임시변수에 할당하라 이고,
castedAmericano의 경우, 만약 coffee가 가리키는 인스턴스 Americano 타입의 인스턴스던지 아니던지CastedAmericano 임시변수에 할당하라 라는 뜻이다.

만약, 컴파일러가 다운캐스팅을 확신할 수 있는 경우에는 as!나 as?대신 as를 사용할 수 있다. 항상 성공하는 것을 아는 경우는 캐스팅하려는 타입이 같은 타입이거나, 부모클래스의 타입이라는 것을 알 때이다.

let castedCoffee: Coffee = yourCoffee as Coffee

Any와 AnyObject의 타입 캐스팅

스위프트에는 특정 타입을 지정하지않고, 여러 타입의 값을 할당할 수 있는 Any와 AnyObject 타입이 존재한다.
Any타입의 경우에는 구조체,열거형,클래스 타입의 값을 할당할 수 있으며, AnyObject는 클래스의 인스턴스만 할당할 수 있다.

func castTypeAppropriate(item: AnyObject){
    if let castedItem: Latte = item as? Latte{
        print(castedItem.description)
    }else if let castedItem: Americano = item as? Americano{
        print(castedItem.description)
    }else if let castedItem: Coffee = item as? Coffee{
        print(castedItem.description)
    }else{
        print("Unknown Type")
    }
}

castTypeAppropriate(item: coffee)
castTypeAppropriate(item: myCoffee)
castTypeAppropriate(item: yourCoffee)

옵셔널과 Any

Any 타입은 모든 값 타입을 표현할 수 있기때문에 옵셔널 타입도 표현할 수 있다. 그런데도 Any 타입에 옵셔널 타입이 들어간다면 스위프트 컴파일러는 경고를 표시한다. 의도적으로 옵셔널 값을 Any 타입의 값으로 사용하고자 한다면 zs 연산자를 통해 타입캐스팅을 해주어야 한다.

let optionalValue: Int? = 100
print(optionalValue)			// 컴파일러 경고 발생
print(optionalValue as Any)		// 경고 없음


profile
개발자 지망생

0개의 댓글