본문으로 바로가기

[Swift] 타입캐스팅 (is, as?, as! 연산자)

category Swift/문법 2022. 4. 9. 20:44

상속과 타입의 포함관계

class AClass {
    var aClassNum: Int = 0
}

class BClass: AClass {
    var bClassNum: Int = 1
}

class CClass: BClass {
    var cClassNum: Int = 2
}

/*
 BClass에는 AClass가 포함돼있고
 CClass에는 BClass가 포함돼있다
 
 즉, CClass의 객체를 만들면 AClass와 BClass의 속성에 접근이 가능하다
 */

let cClass: CClass = CClass()

cClass.aClassNum  // 0
cClass.bClassNum  // 1
cClass.cClassNum  // 2

CClass에 BClass와 AClass가 포함돼있고 반대로 AClass에는 BClass나 CClass가 포함돼있지 않다

is 연산자

타입을 체크하는 연산자

class AClass {
    var aClassNum: Int = 0
}

class BClass: AClass {
    var bClassNum: Int = 1
}

class CClass: BClass {
    var cClassNum: Int = 2
}

let aClass: AClass = AClass()
let bClass: BClass = BClass()
let cClass: CClass = CClass()

// AClass 인스턴스는 BClass/CClass의 타입이 아니다 (AClass 타입이다)
aClass is AClass    // true
aClass is BClass    // false
aClass is CClass    // false

// BClass 인스턴스는 CClass의 타입이 아니다 (AClass/BClass 타입이다)
bClass is AClass    // true
bClass is BClass    // true
bClass is CClass    // false

// CClass 인스턴스는 AClass/BClass/CClass 타입이다
cClass is AClass    // true
cClass is BClass    // true
cClass is CClass    // true

인스턴스 생성과 타입

- 부모클래스 타입으로 자식클래스 인스턴스 생성 가능

class AClass {
    var aClassNum: Int = 0
}

class BClass: AClass {
    var bClassNum: Int = 1
}

class CClass: BClass {
    var cClassNum: Int = 2
}

// CClass의 인스턴스를 생성하지만 타입은 AClass
// AClass가 CClass에 포함돼있기 때문에 가능
let cClass: AClass = CClass()

// 타입은 AClass라도 실제 생성된 인스턴스는 CClass의 인스턴스임
cClass is AClass    // true
cClass is BClass    // true
cClass is CClass    // true

// 하지만 타입은 AClass이기 때문에
// AClass의 속성에만 접근 가능
cClass.aClassNum     // 0
// cClass.bClassNum  (오류)
// cClass.cClassNum  (오류)

업캐스팅 (as 연산자)

- 자식클래스의 인스턴스를 부모클래스 타입으로 인식

- 항상 성공

class AClass {
    var aClassNum: Int = 0
}

class BClass: AClass {
    var bClassNum: Int = 1
}

class CClass: BClass {
    var cClassNum: Int = 2
}

let cClass: CClass = CClass() // CClass의 인스턴스

cClass.aClassNum    // 0
cClass.bClassNum    // 1
cClass.cClassNum    // 2

let aClass: AClass = cClass as AClass // 항상 성공
// cClass를 AClass 타입으로 인식

aClass.aClassNum
// AClass의 타입으로 인식되기 때문에
// AClass 하위타입의 속성에는 접근이 불가능
// aClass.bClassNum  (오류)
// aClass.cClassNum  (오류)

// cClass가 AClass 인스턴스로 변한것이 아닌
// 단순히 AClass 타입으로 인식만 되는것이다
aClass is AClass	// true
aClass is BClass	// true
aClass is CClass	// true

다운캐스팅 (as? / as! 연산자)

- 업캐스팅을 다시 되돌려주는거? 정의를 어찌해야할지 모르겠음

- 다운캐스팅은 업캐스팅이랑 다르게 실패가능성 있음

- as? 연산자

   - 다운캐스팅 성공시 옵셔널 타입으로 반환

   - 다운캐스팅 실패시 nil 반환

   - 언래핑해서 사용

- as! 연산자

   - 성공시 옵셔널타입의 값을 강재로 언래핑해서 반환

   - 실패시 런타임오류

class AClass {
    var aClassNum: Int = 0
}

class BClass: AClass {
    var bClassNum: Int = 1
}

class CClass: BClass {
    var cClassNum: Int = 2
}

let aClass: AClass = CClass() // AClass 타입의 CClass의 인스턴스

aClass.aClassNum    // 0
// aClass.bClassNum    (오류)
// aClass.cClassNum    (오류)

// as? 연산자는 성공시 옵셔널값 반환
// 언래핑해서 사용
// 실패시 nil 반환
if let cClass: CClass = aClass as? CClass {
    cClass.aClassNum    // 0
    cClass.bClassNum    // 1
    cClass.cClassNum    // 2
    
    cClass is AClass    // true
    cClass is BClass    // true
    cClass is CClass    // true
}

// as! 연산자는 성공시 옵셔널값 강제 언래핑해서 반환
// 실패시 런타임오류
let cClass2: CClass = aClass as! CClass

cClass2.aClassNum    // 0
cClass2.bClassNum    // 1
cClass2.cClassNum    // 2

cClass2 is AClass    // true
cClass2 is BClass    // true
cClass2 is CClass    // true