일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 깃허브
- 스위프트
- 플로우차트
- 백준
- 흐름도
- 플로우 차트
- xcode
- PushNotification
- github란
- git이란
- JSONSerialization
- ios
- 정규식
- OS
- flow chart
- git
- 깃
- GitvsGithub
- APNS
- 계산기
- flowchart
- UIViewController
- Regex
- 애플
- Git과 Github차이점
- 순서도
- 정규표현식
- git사용법
- swift
- github
- Today
- Total
Diana의 iOS 개발일기
[스위프트 프로그래밍 3판] - 18. 상속 본문
스위프트에서 클래스는 상속(Inheritance)이 가능합니다. 상속은 객체지향형 프로그래밍에서 꽤나 유용한 기능이죠.
이때 타 클래스로부터 상속을 받는 클래스를 해당 클래스의 자식클래스(Child-class)라고 하며 반대로 자식클래스에게 본인의 특성을 물려준 클래스를 부모클래스(Parents-class)라고 합니다.
상속을 사용하면 자식 클래스는 부모 클래스에 정의된 메서드, 프로퍼티, 서브스크립트 등의 요소들을 사용할 수 있게 되고 자신만의 내용으로 재정의(Override)가 가능하게 됩니다.
또, 원래는 연산프로퍼티가 구현된 클래스에서는 프로퍼티 감시자를 구현할 수 없지만 부모 클래스에서 프로퍼티를 상속받은 자식클래스에서는 연산 프로퍼티나 저장 프로퍼티에 대한 프로퍼티 감시자의 구현이 가능하게 됩니다.
1. 클래스 상속
상속은 위에서 설명했듯이 Base class(기반클래스)를 다른 클래스에서 상속받는 것을 말합니다. 형식은 아래와 같습니다.
class 클래스 이름: 부모클래스 이름{ //뒤에오는 클래스의 기능을 앞에오는 클래스가 상속받음
프로퍼티와 메서드들
}
해당 형식을 사용한 예제는 아래와 같습니다.
class Persion { //Base class
var name: String = ""
var age: Int = 0
var introduction: String {
return "이름: \(name), 나이: \(age)"
}
func speak() {
print("가나다라마바사")
}
}
//--------------------------------------------------------------
class Student: Person {
var grade: String = "F" //Student 고유의 프로퍼티
func study() {
print("Study hard...")
}
}
let yagom: Person = Person()
yagom.name = "yagom"
yagom.age = 99
print(yagom.introduction) //이름: yagom, 나이: 99
yagom.speak() //가나다라마바사
let diana: Student = Student()
diana.name = "diana" //상속을 받았기 때문에 사용이 가능하다
diana.age = 10
diana.grade = "A"
print(diana.introduction) //이름: diana, 나이: 10
diana.speak() //가나다라마바사
diana.study() //Study hard...
위의 예제에서 Student클래스에서는 name, age등의 프로퍼티는 생성하지 않았지만 부모클래스인 Person을 상속받았기 때문에 부모클래스의 프로퍼티를 가져와 사용하는 것이 가능합니다.
이때 자식클래스로 사용된 Student 또한 다른 클래스의 부모클래스가 될 수 있는데 이는 아래 예제와 같습니다.
class UniversityStudent: Student {
var major: String = ""
}
let mia: UniversityStudent = UniversityStudent()
mia.major = "Art"
mia.speak() // 가나다라마바사
mia.study() //Study hard...
이렇게 상속을 사용하면 부모클래스에 있는 프로퍼티 등의 특성들을 자식클래스에서 다시 작성해줄 필요가 없어 효율에 있어 큰 장점을 가지게 됩니다.
하지만 클래스 중에서는 상속을 통한 값 변경에 민감한 경우도 있습니다. 따라서 이 경우는 final 키워드를 사용할 경우 상속을 방지할 수 있습니다.
2. 재정의(Override)
상속을 받은 자식클래스는 부모클래스의 프로퍼티를 받아와 자식클래스 내부에서 값을 변경하여 사용할 수 있습니다. 이를 재정의라고 하며 주로 많은 분들이 오버라이드라고 많이 알고 있습니다. 저는 오버라이드 라는 용어가 더 친숙하므로 해당 용어로 명명하겠습니다.
1. 메서드 재정의
부모클래스로부터 인스턴스 메서드나 타입 메서드를 상속받아 자식클래스에서 재정의 할 수 있습니다.
class Person{
var name: String = ""
var age: Int = 0
var introduction: String {
return "이름: \(name), 나이: \(age)"
}
func speak() {
print("가나다라마바사")
}
class func introductionClass() -> String {
return "인류의 소원은 평화입니다."
}
}
class Student: Person {
var grade: String = "F"
func study() {
print("Study hard...")
}
override func speak(){
print("저는 학생입니다.")
}
}
class UniversityStudent: Student {
var major: String = ""
class func introductionClass() {
print(super.introductionClass())
}
override class func introductionClass() -> String { //위의 메서드와 반환타입이 다르므로 다른 메서드입니다.
return "대학생의 소원은 A+입니다."
}
override func speak(){
super.speak() //부모클래스의 프로퍼티를 변경없이 사용하겠다는 의미네요
print("대학생이죠.")
}
}
let yagom: Person = Person()
yagom.speak() //가나다라마바사
let diana: Student = Student()
diana.speak() //저는 학생입니다.
let mia: UniversityStudent = UniversityStudent()
mia.speak() //저는 학생입니다. 대학생이죠.
print(Person.introduction()) //인류의 소원은 평화입니다.
print(Student.introduction()) //인류의 소원은 평화입니다.
print(UniversityStudent.introductionClass() as String) //as를 사용하여 원하는 반환값을 가지는 메서드를 선정하였습니다.
//대학생의 소원은 A+입니다.
UniversityStudent.introductionClass() as Void //인류의 소원은 평화입니다.
위의 예제에서 UniversityStudent 클래스의 introduction 함수에 대해 하나는 override 키워드가 없고 하나는 있는 이유는 반환 타입이 다른 두 메서드를 구분해주기 위함입니다.
해당 두 메서드는 반환 타입이 다르기 때문에 서로 다른 메서드로 취급되며 출력시 사용하고자 하는 메서드의 반환 타입을 as 키워드를 사용하여 명시해줄 필요가 있습니다.
또한 자식 클래스 내부에서 super 키워드를 사용하여 부모클래스의 프로퍼티를 불러올 경우 자식클래스로 인해 프로퍼티 값이 변경되었더라도 부모 클래스에 선언된 오리지널 프로퍼티의 값을 변경없이 가져올 수 있습니다.
2. 프로퍼티 재정의
메서드와 마찬가지로 자식클래스에서는 부모클래스로부터 상속받은 프로퍼티를 용도에 맞게 오버라이드 하는 것이 가능합니다.
이때 오버라이드가 가능한 프로퍼티는 인스턴스 프로퍼티와 타입 프로퍼티이며 주의해야할 점은 프로퍼티를 오버라이드 한다고 하여도 저장프로퍼티로는 오버라이드가 불가능 합니다.
프로퍼티를 오버라이드 한다는 것은 프로퍼티 자체가 아닌 접근자, 설정자, 프로퍼티 감시자 등을 재정의 하는 것을 의미하며 오버라이드를 할때는 상속받아오는 프로퍼티의 이름과 타입이 생성하고자 하는 프로퍼티와 일치해야 합니다.
class Person {
var name: String = ""
var age: Int = 0
var koreanAge: Int {
return self.age + 1
}
var introduction: String {
return "이름: \(name), 나이: \(age)"
}
}
class Student: Person {
var grade: String = "F"
override var introduction: String {
return super.introduction + "" + "학점: \(self.grade)"
}
override var koreanAge: Int {
get{
return super.koreanAge
}
set{
self.age = newValue - 1
}
}
}
let yagom: Person = Person()
yagom.name = "yagom"
yagom.age = 55
//yagom.koreanAge = 56 //읽기 전용이므로 오류가 발생한다
print(yagom.introduction) //이름: yagom, 나이: 55
print(yagom.koreanAge) //56
let diana: Student = Student()
diana.name = "diana"
diana.age = 14
diana.koreanAge = 15
print(diana.introduction) //이름: diana, 나이: 14, 학점: F
print(diana.koreanAge) //15
3. 프로퍼티 감시자 재정의
class Person {
var name: String = ""
var age: Int = 0 {
didSet{
print("Person age: \(self.age)")
}
}
var koreanAge: Int {
return self.age + 1
}
var fullName: String {
get {
return self.name
}
set {
self.name = newValue
}
}
}
class Student: Person {
var grade: String = "F"
override var age: Int {
didSet {
print("Student age: \(self.age)")
}
}
override var koreanAge:Int {
get {
return super.koreanAge
}
set {
self.age = newValue - 1
}
didSet { } //오류 발생
}
override var fullName: String {
didSet {
print("Full Name: \(self.fullName)")
}
}
}
let yagom: Person = Person()
yagom.name = "yagom"
yagom.age = 55
yagom.fullName = "Jo yagom"
print(yagom.koreanAge) // 56
let diana: Student = Student()
diana.name = "diana"
diana.age = 14 //Person와 Student의 프로퍼티 감시자가 모두 동작
diana.koreanAge = 15
diana.fullName = "Hong diana" //Full Name : Hong diana
print(diana.koreanAge) //15
위의 예제를 보면 Person 클래스에서 상속받은 age 프로퍼티 감시자를 자식 클래스인 Student 클래스에서 오버라이드 해주었는데 이 경우에 Student의 프로퍼티 감시자를 호출하면 부모 클래스와 자식 클래스의 프로퍼티 감시자가 모두 호출됩니다.
4. 서브스크립트 재정의
서브스크립트의 오버라이드는 프로퍼티의 오버라이드와 매우 유사합니다.
서브스크립트의 경우 또한 매개변수와 반환 타입이 다르면 다른 서브스크립트로 정의하므로 오버라이드를 구현할 때 오버라이드하고자 하는 부모 서브스크립트의 매개변수와 반환타입을 잘 유의해서 사용해주어야 합니다.
class School {
var Student: [Student] = [Student]()
subscript(number: Int) -> Student {
print("School subscript")
return students[number]
}
}
class MiddleSchool: School {
var middleStudents: [Student] = [Student]()
override subscript(index: Int) -> Student {
print("MiddleSchool subscript")
return middleStudents[index]
}
}
let university: School = School()
university.students.append(Student())
university[0]
let middle: MiddleSchool = MiddleSchool()
middle.middleStudents.append(Student())
middle[0]
3. 클래스의 이니셜라이저
클래스는 생성하면 값을 초기화 할 이니셜라이저를 생성해주어야 합니다.
클래스의 이니셜라이저에는 크게 지정이니셜라이저(Designaged Initializer)와 편의이니셜라이저(Convenience Initializer)가 있으며 사용 규칙은 아래와 같습니다.
- 자식클래스의 지정 이니셜라이저는 부모클래스의 지정 이니셜라이저를 반드시 호출해야합니다.
- 편의 이니셜라이저는 자신을 정의한 클래스의 다른 이니셜라이저를 반드시 호출해야합니다.
- 편의 이니셜라이저는 궁극적으로 지정 이니셜라이저를 반드시 호출해야합니다.
지정 이니셜라이저
지정 이니셜라이저의 내부에는 클래스의 모든 프로퍼티를 초기화 해야 합니다.
또 상속을 할 때 부모 클래스에 지정이니셜라이저가 구현되어 있고 이 이니셜라이저가 자식 클래스의 프로퍼티들도 모두 초기화 하고 있을 경우 자식클래스는 이니셜라이저를 갖지 않을 수 있습니다.
init(매개변수들) {
초기화 구문
}
편의 이니셜라이저
편의 이니셜라이저는 지정 이니셜라이저 앞에 convenience 키워드를 사용해주어야 합니다.
편의 이니셜라이저는 필수 요소는 아니지만 사용자 정의대로 이니셜라이저를 구현하고 싶은 경우 사용합니다.
convenience init(매개변수들) {
초기화 구문
}
해당 글은 야곰님의 스위프트 프로그래밍 3판 을 기반으로 한 정리 글이며 문제가 있을 시 삭제하도록 하겠습니다.
스위프트 프로그래밍 3판 eBook 구매 링크: www.yes24.com/Product/Goods/81530016
야곰 님의 블로그: blog.yagom.net
'Swift > 책 정리' 카테고리의 다른 글
[스위프트 프로그래밍 3판] - 17. 서브스크립트 (0) | 2021.03.22 |
---|---|
[스위프트 프로그래밍 3판] - 16. 모나드 (0) | 2021.03.21 |
[스위프트 프로그래밍 3판] - 15. 맵, 필터, 리듀스 (0) | 2021.03.18 |
[스위프트 프로그래밍 3판] - 14. 옵셔널 체이닝과 빠른 종료 (1) | 2021.03.17 |
[스위프트 프로그래밍 3판] - 13. 클로저 (0) | 2021.03.11 |