Swift - 19. 타입 캐스팅 Type Casting

지우개·2022년 4월 6일
0

Swift study

목록 보기
8/15
post-thumbnail

타입 캐스팅이란?
인스턴스의 타입을 확인하거나 인스턴스를 같은 계층에 있는 다른 상위 클래스나 하위 클래스로 취급하는 방법

  • is, as를 사용
  • 특정 프로토콜을 따르는지(conforms) 확인할 수 있음

타입 캐스팅을 위한 클래스 계층 구조 선언 Defining a Class Hierarchy for Type Casting

class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
}

// MediaItem 이라는 클래스를 서브 클래싱 해서 두 개의 다른 서브 클래스를 만든다

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)
    }
}

// Movie와 Song 두 개의 클래스를 아이템으로 갖는 library 배열 선언
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]

library가 갖고 있는 Movie, Song 인스턴스의 공통 부모는 MediaItem이기 때문에 library는 타입 추론에 의해 [MediaItem] 배열의 형을 갖게 된다.
타입 지정을 위해서는 downcasting을 이용해야 한다.


타입 검사 Checking Type

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")
// "Media library contains 2 movies and 3 songs" 출력

다운캐스팅 Downcasting

특정 클래스 타입의 상수나 변수는 특정 서브클래스의 인스턴스를 참조하고 있을 수 있다.
as?, as! 연산자를 이용해 어떤 타입의 인스턴스인지 확인

  • as? : 특정 타입이 맞는지 확신할 수 없을 때
    • 하위 클래스 타입으로 다운 캐스트를 시도할 때 옵셔널 값으로 반환
  • as! : 특정 타입이라는 것이 확실한 경우 -> 아닐 경우 런타임 에러
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

캐스팅은 실제 인스턴스나 값을 바꾸는 것이 아니라 지정한 타입으로 취급하는 것이다.


Any와 AnyObject에 대한 타입 캐스팅 Type Casting for Any and AnyObject

Swift에서는 비특정 타입 작업을 위해 2가지를 제공한다:

  • Any는 함수 타입을 포함하여 모든 타입의 인스턴스를 나타낼 수 있다.
  • AnyObject 모든 클래스 타입의 인스턴스를 나타낼 수 있다.

Any 타입의 예제
things라는 Any 타입 배열을 선언해 여러 타입의 값을 저장한다.

var things = [Any]()
things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" })

Any 또는 AnyObject 타입으로만 알려진 상수 또는 변수의 특정 타입을 알아보려면 switch 구문의 케이스로 is 또는 as 패턴을 사용할 수 있다.

for thing in things {
    switch thing {
    case 0 as Int:
        print("zero as an Int")
    case 0 as Double:
        print("zero as a Double")
    case let someInt as Int:
        print("an integer value of \(someInt)")
    case let someDouble as Double where someDouble > 0:
        print("a positive double value of \(someDouble)")
    case is Double:
        print("some other double value that I don't want to print")
    case let someString as String:
        print("a string value of \"\(someString)\"")
    case let (x, y) as (Double, Double):
        print("an (x, y) point at \(x), \(y)")
    case let movie as Movie:
        print("a movie called \(movie.name), dir. \(movie.director)")
    case let stringConverter as (String) -> String:
        print(stringConverter("Michael"))
    default:
        print("something else")
    }
}

// zero as an Int
// zero as a Double
// an integer value of 42
// a positive double value of 3.14159
// a string value of "hello"
// an (x, y) point at 3.0, 5.0
// a movie called Ghostbusters, dir. Ivan Reitman
// Hello, Michael

Any 타입은 옵셔널 타입을 포함한다. 하지만 Swift에서는 Any 타입을 사용해야 하는 곳에 옵셔널을 사용하면 경고를 발생시킨다.
Any 값으로 옵셔널 값을 사용해야 한다면 as 연산자를 사용해 명시적으로 옵셔널을 Any로 캐스트할 수 있다.

let optionalNumber: Int? = 3
things.append(optionalNumber)        // Warning
things.append(optionalNumber as Any) // No warning

0개의 댓글