본문 바로가기
프로그래밍/Swift

[Swift] Type Casting

by 별준 2022. 1. 29.

References

Contents

  • Defining a Class Hierarchy for Type Casting
  • Checking Type
  • Downcasting
  • Type Casting for Any and Any Object

Type casting(타입 캐스팅)은 인스턴스의 타입을 체크하거나 해당 인스턴스를 인스턴스 자신의 클래스 계층에서 다른 수퍼클래스 또는 서브클래스로 처리하는 방법입니다.

Swift에서 타입 캐스팅은 is와 as 오퍼레이터로 구현됩니다. 이 두 오퍼레이터는 값의 타입을 체크하거나 다른 타입으로 그 값을 캐스팅하는 간단하게 효과적인 방법을 제공합니다.

 


Defining a Class Hierarchy for Type Casting

클래스와 서브클래스의 계층 구조가 있는 타입 캐스팅을 사용하여 특정 클래스 인스턴스의 타입을 확인하고 그 인스턴스를 같은 계층에 있는 다른 클래스로 캐스팅할 수 있습니다. 아래의 3개의 코드들은 클래스 계층과 이러한 클래스들의 인스턴스들을 포함하는 배열을 정의하여 타입 캐스팅의 예를 보여줍니다.

 

첫 번째 예제 코드는 MediaItem이라는 기본 베이스클래스를 정의합니다. 이 클래스는 digital media library에 표시되는 모든 종류의 아이템에 대한 기본 기능을 제공합니다. 여기서 구체적으로 String 타입의 이름과 init이라는 이니셜라이저를 선언합니다.

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

 

다음의 코드는 MediaItem의 두 서브클래스를 정의합니다. 첫 번째 서브클래스, Movie는 movie에 대한 추가적인 정보를 캡슐화합니다. 여기에는 director 속성과 이에 대응되는 이니셜라이저가 추가됩니다. 두 번째 서브클래스, Song은 artist 속성과 이니셜라이저가 추가됩니다.

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

마지막으로 library라는 상수 배열을 생성하며, 이 배열은 2개의 Movie 인스턴스와 3개의 Song 인스턴스를 포함합니다. library 배열의 타입은 array literal의 내용을 통해서 초기화됨으로 추론됩니다. Swift의 type checker는 Movie와 Song이 공통의 MediaItem의 서브클래스를 가지고 있다고 추론할 수 있으며, 이 library 배열의 타입을 MediaItem으로 추론합니다.

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 인스턴스입니다. 하지만, 이 배열로 iteration하면, 전달받는 이 아이템들은 Movie나 Song이 아닌 MediaItem 타입입니다. 만약 이들을 native 타입으로 작업하려면, 아래에서 살펴보겠지만 타입을 체크하거나 다른 타입으로 downcasting해야 합니다.

 


Checking Type

type ckeck operator(is)를 사용하여 인스턴스가 어느 특정 서브클래스 타입인지 확인할 수 있습니다. 만약 지정한 서브클래스 타입이라면 true를 리턴하고, 아니라면 false를 리턴합니다.

 

아래 예제 코드는 movieCount와 songCount라는 두 변수를 정의하고 위에서 정의한 libaray 배열에서 Movie와 Song의 인스턴스 개수를 카운트합니다.

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"

 

 

만약 각 반복에서 MediaItem이 Movie 인스턴스라면 item is Movie는 true를 리턴하고 그렇지 않다면 false를 리턴합니다. 유사하게, item is Song은 for문에서의 item 상수가 Song 인스턴스인지 체크합니다. 

 


Downcasting

특정 클래스 타입의 상수나 변수는 실제로 서브클래스의 인스턴스일 수 있습니다. 이러한 경우에 type cast(타입캐스트) 연산자(as? or as!)를 사용하여 서브클래스 타입으로 다운캐스팅할 수 있습니다.

 

다운캐스팅이 실패할 수 있기 때문에 타입캐스트 연산자에는 두 가지 형태가 있습니다. 하나는 as? 이며, 다운캐스팅하려는 타입의 optional값을 리턴합니다. 다른 하나는 as! 이며, 이는 다운캐스팅을 시도하고 강제로 unwrapping합니다.

 

조건 형태인 타입캐스트 연산자인 as?는 다운캐스팅이 성공한다고 확실할 수 없을 때 사용합니다. 이 연산자는 항상 optional 값을 반환하며, 만약 다운캐스팅이 가능하지 않다면 그 값은 nil이 됩니다.

강제 형태인 타입캐스트 연산자 as!는 다운캐스팅이 항상 성공한다고 확신할 때만 사용합니다. 만약 올바르지 않은 클래스 타입으로 다운캐스팅하려고 한다면 런타임 에러를 발생시킵니다.

 

아래 예제 코드는 다시 for-in을 통해 위에서 정의한 library 배열을 순회하고, 각 항목의 description을 출력합니다. 이를 위해서 각 항목을 MediaItem이 아닌 실제 Movie나 Song으로 액세스해야 합니다.

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

 


Type Casting for Any and AnyObject

Swift는 지정되지 않은 타입들에 대해 작업을 할 수 있는 두 개의 특별한 타입을 제공합니다.

  • Any : 함수 타입을 포함한 모든 타입의 인스턴스를 나타냅니다.
  • AnyObject : 모든 클래스 타입의 인스턴스를 나타냅니다.

다음은 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)" })

things 배열은 2개의 Int, 2개의 Double, 하나의 String, 하나의 튜플 (Double, Double), movie 인스턴스, 클로저를 포함합니다.

 

Any 또는 AnyObject 타입에서 알고 있는 상수나 변수의 특정 타입을 찾으려면, switch문에서 is 또는 as 패턴을 사용할 수 있습니다. 아래 예제 코드는 for문으로 things 항목들을 순회하며 각 항목을 switch문으로 쿼리합니다. 일부 switch문의 케이스는 그 값을 지정된 타입의 값으로 바인딩하여 출력합니다.

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

 

 

'프로그래밍 > Swift' 카테고리의 다른 글

[Swift] Protocols (1)  (0) 2022.02.05
[Swift] Extensions  (0) 2022.02.03
[Swift] Optional Chaining  (0) 2022.01.08
[Swift] Deinitialization  (0) 2022.01.06
[Swift] Initialization  (0) 2022.01.03

댓글