타입캐스팅이란
스위프트에서 타입캐스팅이란 변수나 상수의 데이터 타입을 확인하거나
다른 데이터 타입으로 변환하는 것을 말한다.
타입을 변환 한다는 건 쉽게말하면 데이터의 “모양"을 바꾸는 것..
어떤 데이터를 다른 데이터처럼 보이게 만드는 것이다.
타입 캐스팅에는 is
,as
,as!
,as?
총 4가지 연산자가 있는데
is
는 타입 검사(Type Checking)
as
는 업캐스팅(Upcasting)
as!
는 강제 다운캐스팅(Force Downcasting)
as?
는 조건부 다운캐스팅(Optional Downcasting)
이라고 할 수 있다
문법은 다음과 같이 사용 :
변수명 is 타입
변수명 as 타입
변수명 as! 타입
변수명 as? 타입
아래와 같이
class Person {
func brushHair() {
print("머리를 빗습니다")
}
}
class Gildong: Person {
let age = 30
let gender = "male"
override func brushHair() {
print("빗을 머리가 없습니다")
}
func hobby() {
print("누워서 과자를 먹습니다")
}
}
let unknownPerson = Gildong()
부모 클래스 Person과 Person을 상속하는 자식 클래스 Gildong이 있다고 치고
각각 예제와 함께 설명을 정리해 보았다
is : 객체가 특정 타입인지 여부를 검사하여 Boolean값, 즉 true 또는 false로 반환한다
unknownPerson is Person // true
unknownPerson is Gildong // true
unknownPerson
은 Gildong
클래스의 인스턴스이고
unknownPerson
의 타입은 Gildong
이지만, Gildong
클래스는 Person
클래스를 상속하고 있으므로
unknownPerson
은 Person
클래스의 인스턴스이기도 하다 그래서 Person
타입과 Gildong
타입에 둘 다 true를 반환한다
if unknownPerson.gender is Int {
print("gender가 Int타입이라고?")
} else {
print("역시.. gender는 Int가 아니였다능") // "역시.. gender는 Int가 아니였다능"
}
unknownPerson
의 gender
프로퍼티는 문자열이므로 Int
타입이 아니다.
따라서 else를 타서 “역시.. gender는 Int가 아니였다능"이 출력 되었다
as : 하위 클래스 객체를 상위 클래스로 변환하는 작업
let unknownPerson2 = unknownPerson as Person
unknownPerson
은 위에서 만든 Gildong
이란 클래스의 인스턴스이다.
그 unknownPerson
을 as
연산자를 사용하여 Person
으로 업캐스팅 해주어 unknownPerson2
에 집어넣었다
unknownPerson2.brushHair() // 빗을 머리가 없습니다
unknownPerson2.hobby() // Value of type 'Person' has no member 'hobby'
여기서 의문점..
왜 unknownPerson2
의 brushHair()
는 Person의 brushHair()
, “머리를 빗습니다"가 출력되는 것이 아닌
여전히 Gildong
의 brushHair()
, “빗을 머리가 없습니다"로 출력이 되는 걸까..?
이건 Swift의 메소드 호출이 실제 객체 타입을 기반으로 동적 처리되기 떄문이다
위에서 먼저unknownPerson
은 Gildong
클래스의 인스턴스로서 생성이 되었고
그런 다음 unknownPerson
을 업캐스팅해서 unknownPerson2
에 대입했다
unknownPerson2
는 Person 타입의 변수이지만 여전히 Gildong
객체를 가리킨다.
따라서 오버라이딩한 메소드를 호출하면 해당 오버라이딩된 메소드가 실행된다
그래서 Gildong
을 기반으로 호출이 되는 것이다.
반면 unknownPerson2
의 hobby()
라는 메소드를 실행시키려고 하면
오버라이딩되지 않아 Gildong
클래스에만 정의되어 있으므로 호출할 수 없는 것이다.
이런 업캐스팅은 언제 유용하게 사용할수 있을까?
let unknownPerson3 = Person()
let list = [unknownPerson, unknownPerson3]
list[0].hobby() // Value of type 'Person' has no member 'hobby'
만약 위에서 만든 Gildong
타입의 unknownPerson
과
Person
타입의 unknownPerson2
를 하나의 리스트에 담으려고 한다고 해보자
원래 타입이 다르면 오류가 나야 할 텐데 잘 담긴다
그 이유는 swift에서 자동으로 Person
으로 업캐스팅 해주었기 때문이다.
만약 둘이 생판 다른 타입이었다면 에러가 날것이다.
as! : 강제로 타입을 변환하려고 시도하고, 만약 변환이 실패할 경우 런타임 오류가 발생한다
let convertedAge = unknownPerson.age as! Int
print(convertedAge) // 30
let convertedGender = unknownPerson.gender as! Int // Abort() called 에러 발생
print(convertedGender)
as! 연산자를 이용해서 unknownPerson
의 age
와 gender
강제로 각각 Int
로 형 변환하려고 한다
age
는 30이 들어갔기 때문에 정상적으로 Int
로 형 변환되어 문제없이 출력되었으나
gender
는 문자열 “male” 이 들어갔기 때문에 Int
로 형 변환할 수 없고 바로 런타임 에러가 발생하게 된다
이와 같이 as!
연산자는 만약 변환할 수 없는 경우에는 바로 뻗어버리기 때문에
옵셔널 강제 언래핑처럼 반드시 변환 가능 여부가 확실한 경우에만 사용해야 하며
되도록 사용을 지양하는 것이 좋다
as? : 타입 변환을 시도하고, 변환이 실패할 경우에는 nil을 반환한다
let convertedGender = unknownPerson.gender as? Int
print(convertedGender) // nil
if let convertedAge = unknownPerson.age as? Int {
print("값: \(convertedAge), 변환 성공~") // "값: 30, 변환 성공~"
} else {
print("age는 Int로 다운캐스팅 못한다!!")
}
if let convertedGender = unknownPerson.gender as? Int {
print("값: \(convertedGender), 변환 성공~")
} else {
print("gender는 Int로 다운캐스팅 못한다!!") // "gender는 Int로 다운캐스팅 못한다!!"
}
위에서 사용한 as!
연산자 대신에 as?
연산자를 사용함으로써 조건부로 형 변환을 시도할 수 있다.
실패한다 해도 nil을 반환하기 때문에 최소한 에러는 방지되고 안전하게 다운 캐스팅을 시도하는 방법인 것이다