[스위프트 프로그래밍-19장] 타입캐스팅

sanghee·2021년 11월 22일
0
post-thumbnail

이 글은 스위프트 프로그래밍(3판, 야곰 지음)을 읽고 간단하게 정리한 글입니다. 책에 친절한 설명과 관련 예제 코드들이 있으므로 직접 사서 읽기를 추천합니다.

19.0 소개

스위프트는 안전성(Safe)을 중요시하는 언어이므로 데이터 타입 변환을 지원하지 않는다.

*스위프트의 세가지 언어적 특성: 안전성(Safe), 신속성(Fast), 더 나은 표현성(Expressive)

19.1 스위프트의 타입 변환

사실 스위프트에서의 타입 변환은 타입을 변환하는 것이 아니라, 기존 값을 전달인자로 받아 새로운 인스턴스를 생성하는 과정이다.

@frozen public struct Int : FixedWidthInteger, SignedInteger {

    public init(_ source: Float16)
    public init?(exactly source: Float16)
    public init(_ source: Float)
    public init?(exactly source: Float)
    public init(_ source: Double)
    public init?(exactly source: Double)
    public convenience init?<S>(_ text: S, radix: Int = default)
        where S : StringProtocol
    ...

Double 타입인 1.5을 받아서 새로운 Int 구조체를 생성한다. 혹은 문자열과 같이 적절하지 않은 매개변수가 전달된다면 인스턴스 생성이 실패한다.

let int: Int = Int(1.5) // 1
let notInt = Int("a") // nil

19.2 스위프트의 타입캐스팅

스위프트의 타입캐스팅은 인스턴스 타입을 확인하거나 자신을 다른 타입의 인스턴스인양 행세할 수 있다.

커피 클래스

class Coffee {
    let name: String
    let shot: Int
    
    var description: String {
        return "\(shot)\(name)"
    }
    
    init(shot: Int) {
        self.name = "커피"
        self.shot = shot
    }
}

라떼 클래스

class Latte: Coffee {
    let flavor: String
    
    override var description: String {
        return "\(shot)\(flavor) 라떼"
    }
    
    init(flavor: String, shot: Int) {
        self.flavor = flavor
        super.init(shot: shot)
    }
}

아메리카노 클래스

class Americano: Coffee {
    let iced: Bool
    
    override var description: String {
        return "\(shot)\(iced ? "아이스" : "") 아메리카노"
    }
    
    init(iced: Bool, shot: Int) {
        self.iced = iced
        super.init(shot: shot)
    }
}

커피, 라떼, 아메리카노의 클래스 상속도

커피, 라떼, 아메리카노의 클래스 포함도

라떼나 아메리카노는 커피를 상속받기 때문에, 커피가 갖는 특성을 모두 가진다. 따라서 라떼나 아메리카노가 커피인 척 할 수 있다.

19.3 데이터 타입 확인

1. is

📌키워드: is

타입 확인 연산자인 is를 통해서 인스턴스가 어떤 클래스의 인스턴스인지 확인한다.

let coffee = Coffee(shot: 1)
coffee.description // 1샷 커피

let latte = Latte(flavor: "카라멜", shot: 2)
latte.description // 2샷 카라멜 라떼

let americano = Americano(iced: true, shot: 2)
americano.description // 2샷 아이스 아메리카노

coffee is Latte // false
coffee is Americano // false

latte is Coffee // true
americano is Coffee // true

switch문을 이용하여 해당 음료의 타입을 확인한다.

func checkType(_ drink: AnyObject) {
    switch drink {
    case is Latte: print("This is latte")
    case is Americano: print("This is americano")
    case is Coffee: print("This is coffee")
    default: break
    }
}

let latte = Latte(flavor: "카라멜", shot: 2)
checkType(latte) // This is latte

2. Meta Type 타입

메타 타입 타입이란 타입의 타입을 의미한다. 타입의 이름 뒤에 .Type을 붙여 나타낸다. 또는 .self를 붙여 타입이 값으로 표현한 값을 얻을 수 있다.

let int = Int.self
let double = Double.self

int == double // false

3. type(of: )

type(of: )를 통해 타입을 비교한다.

type(of: latte) == Coffee.self // false
type(of: latte) == Latte.self // true

.self

.self 표현은 값 뒤에 써주면 그 값 자신을, 타입 뒤에 쓰먼 타입을 표현하는 값을 반환한다.

"a".self == "a" // true
type(of: 1) == Int.self // true

19.4 다운캐스팅

📌키워드: as? as!

Latte 클래스의 인스턴스가 Coffee인양 행동할 수 있다. 아래와 같이 작성해도 오류가 발생하지 않는다. 그런데 Latte에 있는 flavor를 사용하고 싶을 때가 있다. 그럴 때에는 부모클래스인 Coffee타입을 자식클래스인 Latte타입으로 다운캐스팅해야 한다.

키워드에는 as?와 as!가 있다. 다운캐스팅에 실패할 가능성이 있다면 as?를 사용해야 한다. Coffee 클래스에서 Latte 클래스로 다운캐스팅하였다.

if let latte = fakeCoffee as? Latte {
    print(latte.flavor) // 가짜
}

타입캐스팅의 진짜 의미

타입캐스팅은 실제로 인스턴스를 수정하는 것이 아니라 컴퓨터에게 어떤 타입으로 다뤄야 할지 힌트를 주는 것이다.

19.5 Any, AnyObject의 타입캐스팅

스위프트에는 특정 타입을 지정하지 않고 여러 타입의 값을 할당할 수 있다. Any는 함수 타입을 포함한 모든 타입을 의미한다. AnyObject는 클래스 타입만을 뜻한다. 클래스를 비교하는 것이기에 AnyObject를 사용하였다.

스위프트가 타입에서의 안전성을 중요시하므로 사용을 지양해야 한다.

fakeCoffee의 타입 출력??

fakeCoffee의 타입을 체크하면 커피라고 하는데, type(of:)로 확인하면 라떼라고 뜬다.

func checkType(of item: AnyObject) {
    switch item {
    case let item as Coffee: print("커피입니다")
    case let item as Latte: print("라떼입니다")
    case let item as Americano: print("아메리카노입니다")
    default: print("모르겠어요")
    }
}

checkType(of: coffee) // 커피입니다
checkType(of: fakeCoffee) // 커피입니다
print(type(of: fakeCoffee)) // Latte

옵셔널과 Any

Any타입은 옵셔널을 포함한 모든 값 타입을 표현한다. 그런데도 Any 타입에 옵셔널 값이 들어가면 경고가 뜬다. 흔히 print() 함수를 생각해볼 수 있다.

let optionalInt: Int? = 1
print(optionalInt) // Expression implicitly coerced from 'Int?' to 'Any'
print(optionalInt as Any)
profile
👩‍💻

0개의 댓글