[Swift] 타입 캐스팅

Zoe·2022년 2월 15일
0

Swift

목록 보기
17/21
post-thumbnail

타입 캐스팅


🌟 is, as, as?, as!

🌟 타입 캐스팅 : 인스턴스의 "타입"을 확인 하거나, 해당 인스턴스를 슈퍼 클래스나 하위 클래스로 취급하는 방법

🌟 타입 캐스팅을 사용하여 타입이 프로토콜에 적합한지 여부도 확인

✅ is : Checking Type

표현식 is Type
  • 타입을 체크하는 연산자로, 런타임 시점에 실제 체크가 이루어짐

  • 표현식이 Type과 동일하거나, 표현식이 Type의 서브 클래스인 경우 ➡️ true

  • 이 외의 경우엔 ➡️ false

  • is 연산자는 타입을 체크하는 연산자로, 반환 값은 Bool

1️⃣ 동일한 타입을 확인

let char: Character = "A"

char is Character       // true
char is String          // false
 
 
let bool: Bool = true
 
bool is Bool            // true
bool is Character       // false

2️⃣ 표현식이 Type의 서브 클래스인 경우

class Human { }
class Teacher: Human { }
 
let teacher: Teacher = .init()
teacher is Teacher      // true
teacher is Human        // true
  • teacher이란 인스턴스는 Human 클래스의 서브 클래스이기 때문에 Human으로 타입 체크를 해도 true
class Human {
    let name: String
    init(name: String) {
        self.name = name
    }
}
class Teacher: Human { }
class Student: Human { }
 
 
let people: [Human] = [
    Teacher.init(name: "kim"),
    Student.init(name: "A"),
    Student.init(name: "B")
]
  • Human이란 클래스가 있고 이 Human 클래스를 상속받는 서브클래스 Teacher, Student가 각각 존재

  • people이란 배열에, Teacher 인스턴스 1개, Student 인스턴스 2개를 담았음

🚨 타입에 민감한 Swift에서 어떻게 Teacher & Student 두 개의 타입의 인스턴스를 저장하나 싶겠지만, 이는 업캐스팅으로 가능한 것. TeacherStudent의 슈퍼 클래스가 Human으로 동일하기에, Human이란 클래스로 둘 다 업캐스팅한 것


for human in people {
    if human is Teacher {
        print("teacher : \(human.name)")
    } else if human is Student {
        print("student  : \(human.name)")
    }
}

teacher : kim
student : A
student : B

✅ as: Type Casting

표현식 as  (변환 할)Type
표현식 as? (변환 할)Type
표현식 as! (변환 할)Type
  • 표현식(의 타입)이 변환할 Type과 호환된다면, 변환할 Type으로 캐스팅된 인스턴스를 리턴
  • 상속 관계인 업캐스팅(Upcasting)다운 캐스팅(Downcasting)에서 사용
  • AnyAnyObject 타입을 사용할 경우, 상속 관계가 아니어도 예외적으로 사용

1️⃣ 업캐스팅

class Human {
    let name: String
    init(name: String) {
        self.name = name
    }
}
class Teacher: Human { }
class Student: Human { }
 
 
let people: [Human] = [
    Teacher.init(name: "kim"),
    Student.init(name: "A"),
    Student.init(name: "B")
]
  • Swift는 타입에 민감한 언어이기 때문에, people이란 배열엔 Type Annotation으로 선언한 Human이란 타입의 인스턴스만 들어갈 수 있음
  • Teacher, Student라는 타입의 인스턴스가 도대체 어떻게 들어간 것?
  • 둘의 슈퍼 클래스Human으로 동일하기 때문에, 이 둘을 Human이란 클래스로 업캐스팅 해서 묶어버린 것
class Human {
    let name: String = "Zoe"
}
class Teacher: Human {
    let subject: String = "ComputerScience"
}
class Student: Human {
    let grade: Int = 1
}
let human = Teacher.init() as Human
  • Teacher 타입의 인스턴스를 생성하지만, 이를 Human 타입으로 업캐스팅해서 human에 저장
human.name             // Zoe
human.subject          // Value of type 'Human' has no member 'subject'
  • 업캐스팅 : 서브 클래스의 인스턴스를 슈퍼 클래스의 타입으로 참조하는 것
// 1. as를 사용한 업캐스팅
let human1 = Teacher.init() as Human
 
// 2. Type Annotation을 사용한 업캐스팅
let human2: Human = Teacher.init()

2️⃣ 다운 캐스팅

  • 슈퍼 클래스 인스턴스를 서브 클래스의 타입으로 참조

  • 업캐스팅된 인스턴스를 다시 원래 서브 클래스 타입으로 참조할 때 사용

  • 다운 캐스팅은 실패할 수있기에 as?, as! 연산자를 이용

human.name             // Zoe
human.subject          // Value of type 'Human' has no member 'subject'
var teacher: Teacher = human as! Teacher
  • 다운 캐스팅 : Human 타입으로 업캐스팅된 human 변수를 다시 하위 클래스인 Teacher 타입으로 변환
teacher.subject         // "ComputerScience"
  • Teacher 클래스의 멤버인 subject에도 접근이 가능

🚨 다운 캐스팅은 실패할 수 있음

let human: Human = Teacher.init()
var student: Student = human as! Student
  • Teacher이 아닌, Student로 다운 캐스팅을 해버린 것
  • 에러남
  • 따라서, Swift는 다운캐스팅에 실패할까봐 업캐스팅과 달리 as에 옵셔널(?,!)이 붙은 것
  • as? : "런타임 시점"에 타입 캐스팅(다운 캐스팅)을 하며, 실패할 경우 nil을 리턴
  • as! : "런타임 시점"에 타입 캐스팅(다운 캐스팅)을 하며, 실패할 경우 에러 발생
  • 런타임 시점에 실패한 경우 에러를 발생하므로, 가능하면 as?를 사용하자
for human in people {
    if let teacher as? Teacher {
        print("teacher : \(teacher.name)")
    } else if let student =  human as? Student {
        print("student  : \(student.name)")
    }
}

teacher : kim
student : A
student : B

profile
iOS 개발자😺

0개의 댓글