class Person {
var name: String = ""
func breath() {
print("숨을 쉽니다")
}
}
class Student: Person {
var school: String = ""
func goToSchool() {
print("등교를 합니다")
}
}
class UniversityStudent: Student {
var major: String = ""
func goToMT() {
print("멤버십 트레이닝을 갑니다 신남!")
}
}
var yagom: Person = Person()
var hana: Student = Student()
var jason: UniversityStudent = UniversityStudent()
// is를 사용하여 타입 확인
var result: Bool
result = yagom is Person // true
result = yagom is Student // false
result = yagom is UniversityStudent // false
result = hana is Person // true
result = hana is Student // true
result = hana is UniversityStudent // false
result = jason is Person // true
result = jason is Student // true
result = jason is UniversityStudent // true
// 업 캐스팅
// as를 사용하여 부모클래스의 인스턴스로 사용할 수 있도록 함
var mike: Person = UniversityStudent() as Person
var jenny: Student = Student()
var jina: Any = Person() // as Any 생략 가능
다운 캐스팅은 as?와 as!를 사용하여 자식 클래스의 인스턴스로 사용할 수 있도록 하는 것이다.
// 조건부 다운 캐스팅 - as?를 사용
var optionalCasted: Student?
optionalCasted = mike as? UniversityStudent // mike는 할당된 인스턴스가 UniversityStudent이기 때문에 캐스팅 가능
optionalCasted = jenny as? UniversityStudent // nil
optionalCasted = jina as? UniversityStudent // nil
optionalCasted = jina as? Student // nil
// 강제 다운 캐스팅 - as!를 사용
optionalCasted = mike as! UniversityStudent
optionalCasted = jenny as! UniversityStudent // 런타임 에러
optionalCasted = jina as! UniversityStudent // 런타임 에러
optionalCasted = jina as! Student // 런타임 에러
// assert 함수는 조건에 잘 맞는지 확인할 때 사용할 수 있음
// 디버깅 모드에서만 작동
var someInt: Int = 0
someInt = 1
// 앞의 조건에 맞으면 assert 함수를 넘어가고
// 맞지 않으면 뒤의 문자열을 출력
assert(someInt == 0, "someInt != 0") // 동작 중지, 검증 실패
// 문자열 출력은 생략 가능
assert(someInt == 0) // 동작 중지, 검증 실패
// guard 구문은 Early Exit(빠른 종료)를 위해 사용
func functionWithGuard(age: Int?) {
// optional binding(안전한 unwrapping)과 연결
guard let unwrappedAge = age,
unwrappedAge < 130,
unwrappedAge >= 0 else {
print("나이값 입력이 잘못되었습니다")
// 반드시 return이 있어야 함
return
}
print("당신의 나이는 \(unwrappedAge)세입니다")
}
// protocol은 어떤 기능을 구현하라고 강요하는 것
// 채택: protocol의 기능을 구현한다고 하는 것
// protocol을 준수한다: 실제로 protocol의 기능을 구현했을 때
protocol Talkable {
// 프로퍼티 요구
// 항상 var 키워드 사용
var topic: String { get set } // 읽기, 쓰기 모두 가능
var language: String { get } // 읽기 가능
var language2: String { get }
// 메서드 요구
func talk()
// 이니셜라이저 요구
init(topic: String, language: String)
}
// Person 구조체는 Talkable 프로토콜을 채택
struct Person: Talkable {
var topic: String
let language: String // let은 읽기만 가능, 그러나 var 사용해도 됨
// 읽기 전용 프로퍼티 요구는 읽기 전용 연산 프로퍼티로 대체 가능
var language2: String { return "한국어" }
func talk() {
print("\(topic)에 대해 \(language)로 말합니다")
}
init(topic: String, language: String) {
self.topic = topic
self.language = language
}
}
// 프토토콜은 다중 상속 가능
protocol Readable {
func read()
}
protocol Writeable {
func write()
}
protocol ReadWriteSpeakable: Readable, Writeable {
// func read()
// func write()
func speak()
}
// "클래스의 상속"과 "프로토콜의 채택"을 동시에 하려면
// "클래스의 상속"을 먼저 명시해야 함
class SuperClass: Readable {
func read() { print("read") }
}
class SubClass: SuperClass, Writeable, ReadWriteSpeakable {
func write() { print("write") }
func speak() { print("speak") }
}
// is, as 연산자를 사용하여 프로토콜을 준수하는지 확인할 수 있음
let sup: SuperClass = SuperClass()
let sub: SubClass = SubClass()
var someAny: Any = sup
someAny is Readable // true
someAny is ReadWriteSpeakable // false
if let someReadalbe: Readable = someAny as? Readable {
someReadalbe.read()
} // read
if let someReadWriteSpeakable: ReadWriteSpeakable = someAny as? ReadWriteSpeakable {
someReadWriteSpeakable.speak()
} // 동작하지 않음