타입 캐스팅은 인스턴스 타입을 확인하거나 해당 인스턴스를 자체 클래스에서 다른 상위 클래스나 하위 클래스로 취급하는 방법이다. 타입캐스팅을 사용하여 프로토콜을 준수하는지 확인도 가능하다.
class MediaItem {
var name : String
init(name : String) {
self.name = name
}
}
//다음과 같은 미디어 아이템 클래스를 정의한다.
class Song : MediaItem {
var artist : String
init(name : String, artist : String) {
self.artist = artist
super.init(name : name)
}
}
class Movie : MediaItem {
var director : String
init(name : String, director : String) {
self.director = director
super.init(name : name)
}
}
//위의 미디어 아이템을 상속하는 두개의 하위 클래스를 정의했다.
let library = [Movie(name: "Lee", director : "Micheal"), Song(name : "Park", artist : "songsong")]
//다음과 같은 라이브러리의 경우는 해당 타입을 MediaItem이라는 타입으로 유추한다.
//두 하위 클래스 타입을 포함한 배열이기 때문이다.
var movieCount = 0
var songCount = 0
for item in library {
if item is Movie {
movieCount += 1
} else if item is Song {
songcount += 1
}
}
//다음과 같이 타입을 구분해서 부울 값을 리턴해준다. 타입에 따라서 흐름제어가 가능하다.
for item in library {
if let movie = item as? Movie {
print(movie.name)
} else if song = item as? Song {
print(song.name)
}
}
//다음 코드는 위 코드에서의 라이브러리에 대한 다운캐스팅 코드이다.
//item은 현재 MediaItem의 인스턴스이기 때문에 Movie일수도, Song 일수도 있다. 따라서 옵셔널 다운캐스팅을 수행하는 것이 바람직하다.
// 위 코드는 item에 대해서 nil을 반환하지 않는 값을 받아 출력하는
// 다운캐스팅 루프구문이다. 다운캐스팅 값이 존재하면 이를 받아 출력한다.
var things : [Any] = []
things.append("hi")
things.append(0.0)
things.append(42)
// 다음과 같이 모든 타입의 값을 핸들링 할 수 있다.
for thing in things {
switch thing {
case 0 as Int:
print("Int value")
case 0 as Double:
print("Double value!")
case let someString as String:
print(someString)
//다음과 같이 Any에 대한 타입 캐스팅은 스위치 구문을 통해 접근한다.
//마지막과 같이 특정 값을 명시하지 않아도, 값을 담는 상수를 통해 캐스팅이 가능하다.
let optionalNumber : Int? = 3
things.append(optionalNumber)
things.append(optionalNumber as Any)
//Any타입은 옵셔널 타입을 포함한다. 스위프트는 Any타입의 값이 기대되는 곳에
// 옵셔널 값을 사용하면 경고한다.
//만약 옵셔널 값을 사용하는 게 필요하다면 as연산자를 통해 Any로 명시적으로 옵셔널을 Any로 캐스트 가능하다.