[Swift 문법] Swift 공식 문서 정리 - 19 - 타입캐스팅(TypeCasting)

윤여송·2023년 8월 31일
0

Swift

목록 보기
23/28
post-thumbnail

타입 캐스팅(TypeCasting)

값의 런타임 타입을 정하고 타입의 정보를 제공합니다.

타입 캐스팅(Type casting)은 인스턴스의 타입을 확인하거나 해당 인스턴스를 자체 클래스 계층 구조의 다른 곳에서 다른 상위 클래스 또는 하위 클래스로써 취급하는 방법입니다.

Swift에서 타입 캐스팅은 isas 연산자로 구현됩니다. 이 두 연산자는 값의 타입을 확인하거나 값을 다른 타입으로 캐스트하는 간단하고 효과적인 방법을 제공합니다.

타입 캐스팅을 위한 클래스 계층 정의(Defining a Class Hierarchy for Type Casting)

클래스와 하위 클래스의 계층도와 함께 타입 캐스팅을 사용하여 특정 클래스 인스턴스의 타입을 확인하고 같은 계층도 내에서 다른 클래스로 인스턴스를 캐스트 할 수 있습니다. 아래의 세 코드는 타입 캐스팅의 예제에서 사용하기 위해 클래스의 계층도와 해당 클래스의 인스턴스를 포함하는 배열을 정의합니다.

첫번째 코드는 MediaItem 이라는 새로운 기본 클래스를 정의합니다. 이 클래스는 디지털 미디어 라이브러리에 나타나는 모든 종류의 항목에 대한 기본 기능을 제공합니다. 특히 String 타입의 name 프로퍼티와 init(name:) 초기화 구문을 선언합니다.(영화와 노래를 포함하여 모든 미디어 항목은 이름을 가지고 있다고 가정합니다.)

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

다음 코드는 MediaItem의 두 개의 하위 클래스를 정의합니다. 첫번째 하위 클래스 Movie는 영화 또는 필름에 대한 추가 정보를 캡슐화 합니다. 기본 MediaItem 클래스의 상위에 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)
    }
}

마지막 코드는 두 개의 Movie 인스턴스와 세 개의 Song 인스턴스를 포함하는 library 라는 배열 상수를 생성합니다. libaray 배열의 타입은 배열 리터럴의 내용으로 초기화하여 추론합니다. Swift의 타입 검사기는 MovieSongMediaItem 의 상위 클래스를 공통으로 가지고 있으므로 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: "Chensney Hawkes"),
    Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
// the type of "library" is inferred to be [MediaItem]

library에 저장된 항목은 여전히 MovieSong 인스턴스 입니다. 그러나 이 배열의 항목을 반복하면 항목은 Movie 또는 Song이 아닌 MediaItem 타입으로 받습니다. 기본 타입으로 작업을 하려면 아래에 설명된대로 타입을 확인하거나 다른 타입으로 다운캐스트 해야합니다.

타입 검사(Checking Type)

인스턴스가 특정 하위 클래스 타입인지 확인하기 위해 타입 검사 연산자(type check operator)(is)를 사용합니다. 이 타입 검사 연산자는 인스턴스가 하위 클래스 타입이면 true 아니면 false 를 반환합니다.

아래의 예제는 library배열에 MovieSong 인스턴스의 숫자를 나타내는 두 개의 변수 movieCountsongCount를 정의합니다.

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 배열의 모든 항목을 통해 반복합니다. 반복문 실행 시에 for-in 루프는 배열에서 다음 MediaItemitem 상수에 설정합니다.

item is Movie는 현재 MediaItemMovie 인스턴스이면 true를 반환하고 아니면 false를 반환합니다. 유사하게 item is Song은 항목이 Song 인스턴스인지 확인합니다. for-in루프의 마지막에 movieCountsongCount의 값은 MediaItem 인스턴스에 각 타입을 포함하는 카운트를 나타냅니다.

다운 캐스팅(Downcasting)

특정 클래스 타입의 상수 또는 변수는 하위 클래스의 인스턴스를 참조할 수 있습니다. 이것이 필요하다고 생각하는 경우 타입 캐스트 연산자(type cast operator)(as? 또는 as!)를 사용하여 하위 클래스 타입으로 다운 캐스트(downcast) 할 수 있습니다.

다운 캐스팅은 실패할 수 있으므로 타입 캐스트 연산자는 2가지 다른 형태로 제공됩니다. 조건부 형식 as?은 다운 캐스트를 하려고 할 때 타입의 옵셔널 값을 반환합니다. 강제 형식 as!은 다운 캐스트를 시도하고 단일 복합 동작으로 강제 언래핑합니다.

다운 캐스트가 성공할지 확신이 없을 때 조건부 형식의 타입 캐스트 연산자(as?)를 사용합니다. 이 연산자의 형식은 항상 옵셔널 값을 반환하고 다운 캐스트가 불가능하면 nil을 반환합니다. 이것은 다운 캐스트의 성공 여부를 확인하는 용도로도 사용 가능합니다.

다운 캐스트가 항상 성공할 것이라는 확신이 있을 때만 강제 형식의 타입 캐스트 연산자 (as!)를 사용해야 합니다. 이 연산자의 형식은 유효하지 않은 클래스 타입으로 다운 캐스트를 시도하면 런타임 에러가 발생합니다.

아래의 예제는 library에 각 MediaItem을 반복하고 각 항목에 대한 적절한 설명을 출력합니다. 이렇게 하려면 MediaItem이 아닌 Movie 또는 Song으로 각 항목에 접근해야 합니다. 이것은 설명을 사용하기 위해 Movie 또는 Songdirector 또는 artist 프로퍼티에 접근할 수 있도록 하기위해 필요합니다.

이 예제는 배열의 각 항목은 Movie 또는 Sogn일 수 있습니다. 각 항목에 사용할 실제 클래스를 미리 알지 못하므로 루프를 통해 매번 다운 캐스트를 확인하기 위해 타입 캐스트 연산자 (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로 현재 item을 다운 캐스트 하는 것으로 시작합니다. itemMediaItem 인스턴스 이므로 Movie 일 수도 있고 마찬가지로 Song 일 수 있습니다. 또는 기본 MediaItem일 수도 있습니다. 이 불확실성 때문에 타입 캐스트 연산자의 as? 형식은 하위 클래스 타입으로 다운 캐스트를 시도할 때 옵셔널 값으로 반환합니다. item as? Movie의 결과는 Movie? 타입 또는 "옵셔널 Movie" 입니다.

라이브러리 배열에 Song 인스턴스로 적용할 때 Movie로 다운 캐스팅은 실패합니다. 이것을 대응하기 위해 위의 예제는 옵셔널 Movie가 실제 값에 포함되어 있는지 확인하기 위해 (다운 캐스트가 성공 되었는지 확인하기 위해) 옵셔널 바인딩을 사용합니다. 이 옵셔널 바인딩은 "if let movie = item as? Movie"로 작성되고 아래와 같이 읽을 수 있습니다.

"Movieitem은 접근혀라고 합니다. 성공하면 반환된 옵셔널 Moviemovie라는 새로운 임시 상수로 설정합니다.

다운 캐스팅이 성공하면 movie의 프로퍼티는 director의 이름을 포함하여 Movie 인스턴스에 대해 설명을 출력하는데 사용합니다. 유사한 원칙을 사용하여 Song 인스턴스를 확인하고 라이브러리에서 Song을 찾을 때마다 artist 이름을 포함하여 적절한 설명을 출력하는데 사용합니다.

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

Swift는 비특정 타입 작업을 위해 2개의 특별한 타입을 제공합니다.

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

제공하는 동작과 기능이 명시적으로 필요한 경우에만 AnyAnyObject를 사용합니다. 코드에서 작업할 것으로 예상되는 타입에 대해 구체적으로 지정하는 것이 항상 좋습니다.

다음은 함수 타입과 비 클래스 타입을 포함하여 다른 타입에 혼합으로 작업하기 위해 Any를 사용하는 예제입니다. 이 예제는 Any 타입에 값을 저장할 수 있는 things라는 배열을 생성합니다.

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) 타입의 튜플, "Ghostbusters"영화, 그리고 String 값과 다른 String값을 반환하는 클로저 표현식을 포함합니다.

Any 또는 AnyObject 타입으로만 알려진 상수 또는 변수의 특정 타입을 알아보려면 switch 구문의 케이스로 is또는 as패턴을 사용할 수 있습니다. 아래의 예제는 things 배열에 항목을 반복하고 switch 구문으로 각 항목의 타입을 조회합니다. 몇몇의 switch 구문의 케이스는 일치된 값을 지정된 타입의 상수에 바인딩하여 해당 값을 출력할 수 있도록 합니다.

profile
y_some__velog

0개의 댓글