Type Casting

Groot·2022년 8월 24일
0

Swift Language Guide

목록 보기
18/24
post-thumbnail

Type Casting

  • Type Casting은 인스턴스의 유형을 확인하거나 해당 인스턴스를 자체 클래스 계층 구조의 다른 위치에서 다른 수퍼 클래스 또는 하위 클래스로 처리하는 방법입니다.
  • Swift의 Type Casting은 is 및 as 연산자로 구현됩니다. 이 두 연산자는 값의 유형을 확인하거나 값을 다른 유형으로 캐스팅하는 간단하고 표현적인 방법을 제공합니다.
  • 프로토콜 적합성 검사에 설명된 대로 Type Casting을 사용하여 유형이 프로토콜을 준수하는지 여부를 확인할 수도 있습니다.

📌 Defining a Class Hierarchy for Type Casting

  • 클래스 및 하위 클래스의 계층 구조와 함께 Type Casting을 사용하여 특정 클래스 인스턴스의 유형을 확인하고 해당 인스턴스를 동일한 계층 구조 내의 다른 클래스로 캐스팅할 수 있습니다.

  • 아래의 세 가지 코드 조각은 Type Casting의 예에서 사용하기 위해 클래스의 계층 구조와 해당 클래스의 인스턴스를 포함하는 배열을 정의합니다.

  • 첫 번째 소스 코드은 MediaItem이라는 새 기본 클래스를 정의합니다. 이 클래스는 디지털 미디어 라이브러리에 나타나는 모든 종류의 항목에 대한 기본 기능을 제공합니다.

  • 특히, String 유형의 이름 속성과 초기화 이름 이니셜라이저를 선언합니다. (모든 영화와 Song를 포함한 모든 미디어 항목에는 이름이 있다고 가정합니다.)

    class MediaItem {
        var name: String
        init(name: String) {
            self.name = name
        }
    }
  • 다음 소스 코드은 MediaItem의 두 하위 클래스를 정의합니다. 첫 번째 하위 클래스인 Movie는 영화나 영화에 대한 추가 정보를 캡슐화합니다

  • 해당 이니셜라이저와 함께 기본 MediaItem 클래스 위에 director 속성을 추가합니다. 두 번째 하위 클래스인 Song은 기본 클래스 위에 아티스트 속성과 초기화 프로그램을 추가합니다.

    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)
        }
    }
  • 마지막 소스 코드은 2개의 Movie 인스턴스와 3개의 Song 인스턴스를 포함하는 library라는 상수 배열을 생성합니다.

  • 라이브러리 배열의 유형은 배열 리터럴의 내용으로 초기화하여 유추됩니다.

  • Swift의 유형 검사기는 Movie와 Song이 MediaItem의 공통 수퍼클래스를 가지고 있다는 것을 추론할 수 있으므로 라이브러리 배열에 대해 [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]
  • 라이브러리에 저장된 항목은 여전히 장면 뒤에서 Movie 및 Song 인스턴스입니다.
  • 그러나 이 배열의 내용을 반복하는 경우 다시 받는 항목은 Movie 또는 Song이 아닌 MediaItem으로 유형이 지정됩니다.
  • 그것들을 기본 유형으로 사용하려면 아래에 설명된 대로 유형을 확인하거나 다른 유형으로 다운캐스트해야 합니다.

📌 Checking Type

  • 유형 검사 연산자(is)를 사용하여 인스턴스가 특정 하위 클래스 유형인지 여부를 확인합니다.
  • 유형 검사 연산자는 인스턴스가 해당 하위 클래스 유형이면 true를 반환하고 그렇지 않으면 false를 반환합니다.
  • 아래 예제는 라이브러리 배열의 Movie 및 Song 인스턴스 수를 계산하는 두 개의 변수, movieCount 및 songCount를 정의합니다.
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"
  • 이 예제는 라이브러리 배열의 모든 항목을 반복합니다.
  • 각 패스에서 for-in 루프는 항목 상수를 배열의 다음 MediaItem으로 설정합니다.
  • item is Movie는 현재 MediaItem이 Movie 인스턴스이면 true를 반환하고 그렇지 않으면 false를 반환합니다.
  • 마찬가지로 item is Song은 항목이 Song 인스턴스인지 확인합니다.
  • for-in 루프의 끝에서 movieCount 및 songCount 값에는 각 유형에서 발견된 MediaItem 인스턴스의 수가 포함됩니다.

📌 Downcasting

  • 특정 클래스 유형의 상수 또는 변수는 실제로 배후에서 하위 클래스의 인스턴스를 참조할 수 있습니다.
  • 이것이 True라고 생각되는 경우 유형 캐스트 연산자(as? 또는 as!)를 사용하여 하위 클래스 유형으로 다운캐스트를 시도할 수 있습니다.
  • 다운캐스팅이 실패할 수 있기 때문에 유형 캐스트 연산자는 두 가지 다른 형식으로 제공됩니다.
  • 조건부 형식(as?)은 다운캐스트하려는 유형의 선택적 값을 반환합니다.
  • 강제 형식 as!는 다운캐스트를 시도하고 결과를 단일 복합 동작으로 강제 해제합니다.
  • 다운캐스트가 성공할지 확신할 수 없는 경우 유형 캐스트 연산자(as?)의 조건부 형식을 사용합니다.
  • 이 형식의 연산자는 항상 선택적 값을 반환하고 다운캐스트가 가능하지 않은 경우 값은 nil이 됩니다. 이를 통해 성공적인 다운캐스트를 확인할 수 있습니다.
  • 다운캐스트가 항상 성공할 것이라고 확신하는 경우에만 형식 캐스트 연산자의 강제 형식(as!)을 사용합니다.
  • 이 형식의 연산자는 잘못된 클래스 유형으로 다운캐스트하려고 하면 런타임 오류를 트리거합니다.
  • 아래 예제는 라이브러리의 각 MediaItem을 반복하고 각 항목에 대한 적절한 설명을 인쇄합니다.
  • 이렇게 하려면 각 항목에 MediaItem이 아니라 실제 영화 또는 Song로 액세스해야 합니다.
  • 설명에 사용하기 위해 영화 또는 Song의 감독 또는 아티스트 속성에 액세스할 수 있으려면 이 작업이 필요합니다.
  • 이 예에서 배열의 각 항목은 Movie일 수도 있고 Song일 수도 있습니다.
  • 각 항목에 어떤 실제 클래스를 사용할지 미리 알지 못하므로 루프를 통해 매번 다운캐스트를 확인하기 위해 형식 캐스트 연산자(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
  • 예제는 현재 항목을 영화로 다운캐스팅하는 것으로 시작합니다. item은 MediaItem 인스턴스이기 때문에 Movie일 수 있습니다.
  • 마찬가지로 Song 또는 기본 MediaItem일 수도 있습니다.
  • 이러한 불확실성 때문에 as? 형식 캐스트 연산자의 형식은 하위 클래스 형식으로 다운캐스트를 시도할 때 선택적 값을 반환합니다.
  • 항목의 결과는? Movie는 Movie? 또는 "Optional Movie" 유형입니다.
  • 라이브러리 배열의 Song 인스턴스에 적용하면 Movie로 다운캐스팅이 실패합니다.
  • 이에 대처하기 위해 위의 예제에서는 옵셔널 바인딩을 사용하여 옵셔널 Movie에 실제로 값이 포함되어 있는지 확인합니다(즉, 다운캐스트가 성공했는지 확인하기 위해).
  • 이 선택적 바인딩은 "if let movie = item as? 영화"로 읽을 수 있습니다.
  • 다운캐스팅이 성공하면 영화의 속성을 사용하여 감독 이름을 포함하여 해당 Movie 인스턴스에 대한 설명을 인쇄합니다.
  • Song 인스턴스를 확인하고 라이브러리에서 Song를 찾을 때마다 적절한 설명(아티스트 이름 포함)을 인쇄하는 데 유사한 원칙이 사용됩니다.

    캐스팅은 실제로 인스턴스를 수정하거나 값을 변경하지 않습니다. 기본 인스턴스는 동일하게 유지됩니다. 그것은 단순히 그것이 캐스트 된 유형의 인스턴스로 취급되고 액세스됩니다.

📌 Type Casting for Any and AnyObject

  • Swift는 비특정 유형 작업을 위한 두 가지 특수 유형을 제공합니다.
    • Any는 함수 유형을 포함하여 모든 유형의 인스턴스를 나타낼 수 있습니다.
    • AnyObject는 모든 클래스 유형의 인스턴스를 나타낼 수 있습니다.
  • Any 및 AnyObject가 제공하는 동작과 기능이 명시적으로 필요한 경우에만 사용하십시오.
  • 코드에서 작업할 것으로 예상되는 유형에 대해 구체적으로 설명하는 것이 항상 더 좋습니다.
  • 다음은 함수 유형 및 비클래스 유형을 포함하여 다양한 유형을 혼합하여 작업하기 위해 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 배열에는 두 개의 Int 값, 두 개의 Double 값, String 값, (Double, Double) 유형의 튜플, 영화 "Ghostbusters" 및 String 값을 사용하고 다른 String 값을 반환하는 클로저 표현식이 포함됩니다.
  • Any 또는 AnyObject 유형으로만 알려진 상수 또는 변수의 특정 유형을 검색하려면 switch 문의 경우에 is 또는 as 패턴을 사용할 수 있습니다.
  • 아래 예제는 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는 Any 유형의 값이 예상되는 선택적 값을 사용하는 경우 경고를 표시합니다.
선택적 값을 Any 값으로 사용해야 하는 경우 아래와 같이 as 연산자를 사용하여 선택적 값을 Any로 명시적으로 캐스팅할 수 있습니다.

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

0개의 댓글