타입 캐스팅은 스위프트 말고 다른 프로그래밍 언어에서도 널리 쓰이는 개념인데, 인스턴스의 타입을 확인하거나(is
) 슈퍼클래스 또는 서브클래스 타입처럼 다루기 위해(as
) 사용하는 것을 말한다.
간단하게 말하면 형변환 이라고 할 수 있겠다.
출처 : 스위프트 공식 문서
타입 캐스팅을 위한 예제로 미디어라는 클래스를 정의한다.
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
MediaItem
클래스는 String 형식의 name 프로퍼티와 생성자 하나를 갖는 클래스다.
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
class Song: MediaItem {
var artist: String
init(name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}
그리고 MediaItem
클래스를 상속받는 Movie
클래스와 Song
클래스.
그러니까 구조는 미디어는 미디어와 미디어를 만든 사람이라는 형식이 존재하는 거고, 미디어 안에는 음악과 영화가 있는, 그런 구조다.
let library = [
Movie(name: "Casablanca", director: "Michael Curtiz"),
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
Movie(name: "Citizen Kane", director: "Orson Welles"),
Song(name: "The One And Only", artist: "Chesney Hawkes"),
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
// the type of "library" is inferred to be [MediaItem]
그리고 라이브러리 배열안에 아까 선언한 클래스들을 이용해서 영화와 노래들을 넣어주는 거다. 카사블랑카라는 영화와 엘비스 프레슬리의 노래 등등.. 나는 모르는 거지만 암튼 그렇다! 그리고 Movie
와 Song
클래스 들은 MediaItem
을 상속받는 클래스들이기 때문에 자연스럽게 library 배열은 MediaItem
타입의 배열이 된다.
타입을 확인할 때 is
라는 연산자를 사용한다. (원문에서는 Use the type check operator (is) 라고 되어있다. )
예제를 이용하여 간단히 설명하면
var movieCount = 0
var songCount = 0
for item in library {
if item is Movie {
movieCount += 1
} else if item is Song {
songCount += 1
}
}
print("Media library contains \(movieCount) movies and \(songCount) songs")
// prints
>>> "Media library contains 2 movies and 3 songs"
for문을 돌면서 라이브러리 안에 있는 item이 Movie
클래스 형식이면 movieCount 가 증가하고, 그게 아니라 Song
클래스 형식이면 songCount가 증가한다. 출력은 당연히 2개의 Movie와 3개의 Songs가 나온다. 이게 is
연산자의 사용 방식이다. 뭔가 그냥 영어 문장을 읽는 듯한 자연스러움이라 굉장히 신기했다.
나는 Checking type 보다는 Downcasting을 훨씬 많이 보았다.
원문은
A constant or variable of a certain class type may actually refer to an instance of a subclass behind the scenes. Where you believe this is the case, you can try to downcast to the subclass type with a type cast operator (as? or as!).
이라고 되어있는데, 직역하면 타입 캐스트 연산자인 as?
나 as!
를 이용하여 인스턴스를 동일한 계층구조 내의 다른 클래스로 타입 캐스팅 할 수 있다? 이런 내용이다. 쉽게 얘기하면 우리가 string num을 (int)num 이런식으로 쓰는 방식을 클래스에 적용하는 방식이라고 보면 될 듯 하다.
바로 예제로 들어가보면
for item in library {
if let movie = item as? Movie {
print("Movie: \(movie.name), dir. \(movie.director)")
} else if let song = item as? Song {
print("Song: \(song.name), by \(song.artist)")
}
}
// Movie: Casablanca, dir. Michael Curtiz
// Song: Blue Suede Shoes, by Elvis Presley
// Movie: Citizen Kane, dir. Orson Welles
// Song: The One And Only, by Chesney Hawkes
// Song: Never Gonna Give You Up, by Rick Astley
library 라는 MediaItem
타입의 배열안에 있는 요소들을 하나씩 살펴보면서 Movie
클래스로 다운캐스팅 되는 경우, Song
클래스로 다운 캐스팅 되는 경우를 살펴보는 예제이다.
그리고 as?
는 옵셔널 타입의 값을 반환하기 때문에 if let 구문으로 값을 꺼내온 것을 볼 수 있다.
as!
의 경우는 강제로 unwrap 하는 것이기 때문에 다운캐스팅이 항상 성공할 경우에만 사용 가능하다. 그렇지 않으면 런타임 에러가 발생한다. 고로, 막연한 사용은 굉장히 위험하기 때문에 if let 구문을 사용하는 것을 추천한다.