swift-12 타입 캐스팅

영점·2022년 10월 12일
0

Swift_Study

목록 보기
12/12

스터디를 진행하며 처음부터 다시 Swift를 공부하고 있습니다.
오늘 작성할 파트는 타입캐스팅 입니다.

본래 순서라면.. 초기화를 다하고 하려했지만
초기화의 기초는 익혔기 때문에 다음 개념을 먼저 공부하고자 타입캐스팅을 작성하게 되었습니다.

class MediaItem {
    var name: String
    init(name: String) {
        self.name = 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 클래스와 그 클래스를 상속한 MovieSong 클래스가 있다.

let library : [MediaItem] = [ 
    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]

libraryMovieSong의 슈퍼 클래스가 MediaItem으로 동일하기에,
MediaItem이란 클래스로 둘 다 업캐스팅한 것이다.

기본 타입으로 작업을 하려면 타입을 확인하거나 다른 타입으로 다운캐스트 해야한다.

타입 확인 is

인스턴스가 특정 하위 클래스 타입인지 확인하기 위해 타입 검사 연산자(is)를 사용한다.

리턴타입은 Bool이다.

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"

library에 저장된 Movie와 Song이 몇개인지 루프와 조건을 돌려 이를 출력한다.

업캐스팅 as

업캐스팅은 서브 클래스의 인스턴스를 슈퍼 클래스의 타입으로 참조하는 것을 말한다.

해당 개념을 설명하기 위해 예제를 들자면,

let theory = Movie.init(name: "cats", director: "Risk") as MediaItem
print("Movie : \(theory.name), dir : \(theory.director)")

해당 theory를 프린트하면 Value of type 'MediaItem' has no member 'director' 에러가 뜬다.

위 코드는 Movie인스턴스를 생성하지만, 이를 as를 사용하여 MediaItem으로 업캐스팅해서 theory
상수에 저장하겠다는 의미이기 때문이다.

좀 더 풀어서 설명하자면, theoryMovie란 서브클래스를 MediaItem이란 슈퍼클래스로 업캐스팅을 했기 때문에, theoryMidiaItem에 있는 멤버밖에 접근을 하지 못하게 된 것이다.

다운캐스팅 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

예시의 경우 Movie와 Song 두 가지 선택지가 있어, 각 항목에 사용할 실제 클래스를 미리 알지 못하므로,
as?를 사용하는 것이 적절하다.

Any & AnyObject 타입캐스팅

Any 는 모든 타입의 인스턴스를 나타낼 수 있고,
AnyObject 는 모든 클래스 타입의 인스턴스를 나타낼 수 있다.

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

as는 업캐스팅 말고도 패턴 매칭에도 사용되는데,
switch 문을 활용해서, 해당 타입일 경우(캐스팅에 성공한 경우) case문이 실행될 수 있게 할 수 있다.

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 & AnyObject

Any와 AnyObject는 런타임 시점에 타입이 결정되기 때문에 컴파일 시점에는 해당 타입을 알 수 없다.
때문에 해당 타입의 멤버에 접근할 수 없다. 접근하려면 다운캐스팅을 사용하여야 한다.

제공하는 동작과 기능이 명시적으로 필요한 경우에만 Any 와 AnyObject 를 사용해야하고,
코드에서 작업할 것으로 예상되는 타입에 대해 구체적으로 지정하는 것이 좋다.
( Swift가 타입에 민감한 언어라 그런 것도 있긴함. )

if let movie = item as? Movie {
	movie.append("credit_")
}

뭐.. 이런식으로..

profile
일단 배운내용은 적어두기

0개의 댓글