상속

홍석현·2022년 8월 18일
0
post-thumbnail

클래스는 다른 클래스로부터 상속받을 수 있습니다.
상속을 받은 클래스는 자식클래스(Subclass, Child-class),
자신의 특성을 물려준, 상속해준 클래스는 부모클래스(Superclass, parents-class)라고 표협합니다.
다른 클래스로부터 상속을 받지 않은 클래스를 기반클래스(Base Class)라고 부릅니다.

class Person {
    var name: String = String()
    var age: Int = 0
    
    var introduction: String {
        return "이름: \(name), 나이: \(age)"
    }
    
    func speak() {
        print("저는 말을 할 줄 압니다.")
    }
}

위 Person 클래스는 다른 클래스로 부터 상속을 받지 않으므로 기반클래스입니다.
이제 Person을 통해 상속을 하면서 클래스의 상속에 대해 알아봅시다.

클래스 상속

상속은 클래스를 수직으로 확장할 수 있도록 합니다.

상속은 클래스의 세미콜론 뒤에 상속받을 클래스의 이름을 써주면 뒤에 오는 클래스의 기능을 앞의 클래스가 상속받을 것임을 뜻합니다.

class Student: Person {
    var grade: String = "F"
    
    func study() {
        print("공부를 열심히 합시다...")
    }
}

let peter = Person()
peter.name = "피터"
peter.age = 30
print(peter.introduction) // 이름: 피터, 나이: 30

let hong = Student()
hong.name = "홍"
hong.age = 35
hong.grade = "B+"
print(hong.introduction) // 이름: 홍, 나이: 35
hong.speak() // 저는 말을 할 줄 압니다.
hong.study() // 공부를 열심히 합시다...

Person 클래스를 상속받은 Student 클래스는 Person의 인스턴스 메서드, 타입 메서드, 인스턴스 프로퍼티, 타입 프로퍼티, 서브스크립트 등 모든 특성을 포함합니다.

다른 클래스를 상속받으면 똑같은 기능을 구현하기 위하여 코드를 다시 작성할 필요가 없으므로 코드를 재사용하기 용이하고 더불어 기능을 확장할 때 기존 클래스를 변경하지 않고도 새로운 추가 기능을 구현한 클래스를 정의할 수 있습니다.

재정의

자식클래스는 부모클래스로부터 물려받은 특성(인스턴스 메서드, 타입 메서드, 인스턴스 프로퍼티, 타입 프로퍼티, 서브스크립트 등)을 그대로 사용하지 않고 자신만의 기능으로 변경하여 사용할 수 있습니다. 이를 재정의(Override)라고 합니다.

상속받은 특성들을 재정의 하려면 새로운 정의 앞에 override라는 키워드를 사용합니다.

만약 자식클래스에서 부모클래스의 특성을 재정의했을 때, 부모클래스의 특성을 자식클래스에서 사용하고 싶다면 super 프로퍼티를 사용하면 됩니다. 즉, 자식클래스에서 특성을 재정의했지만 필요에 따라 부모클래스의 특성을 활용하고 싶을 때 super를 사용합니다.

// 재정의 메소드의 경우
override func someMethod() {
	super.someMethod()
}

// 부모클래스의 서브스크립트 사용
super[index]

메서드 재정의

class Person {
    var name: String = String()
    var age: Int = 0
    
    var introduction: String {
        return "이름: \(name), 나이: \(age)"
    }
    
    func speak() {
        print("저는 말을 할 줄 압니다.")
    }
}

class Student: Person {
    var grade: String = "F"
    
    func study() {
        print("공부를 열심히 합시다...")
    }
}

class UniversityStudent: Student {
    override func study() {
        super.study()
        
        print("대학생은 밤새서 공부...하고 싶지 않아")
    }
    
    override func speak() {
        print("(이젠 말하고 싶지 않지만)")
        super.speak()
    }
    
    override var introduction: String {
        return "저는 이제 대학생 \(name)이고, 나이는 \(age)살입니다."
    }
}

let universityStudent = UniversityStudent()
universityStudent.name = "효성"
universityStudent.age = 21
universityStudent.study()
// 공부를 열심히 합시다...
// 대학생은 밤새서 공부...하고 싶지 않아

universityStudent.speak()
// 이젠 말하고 싶지 않지만
// 저는 말을 할 줄 압니다.
print(universityStudent.introduction)
// 저는 이제 대학생 효성이고, 나이는 21살입니다.

프로퍼티 재정의

인스턴스 프로퍼티나 타입 프로퍼티 또한 메서드와 마찬가지로 부모클래스로부터 상속받는 다면 용도에 맞게 설정할 수 있습니다.

  • 저장 프로퍼티로 재정의할 수는 없습니다.
    • 프로퍼티를 재정의한다는 것은 프로퍼티 자체가 아니라 프로퍼티에 접근자(getter), 설정자(setter), 프로퍼티 감시자 등을 재정의한다는 것을 의미합니다.
  • 읽기 전용 프로퍼티는 읽기 쓰기가 모두 가능한 프로퍼티 재정의 🟢
  • 읽기 쓰기가 모두 가능한 프로퍼티가 읽기 전용 프로프티로 재정의 ❌
    • 읽기 쓰기 모두 가능한 프로퍼티의 재정의를 설정자만 따로 재정의할 수 없고 접근자, 설정자 모두 재정의해야 합니다.
    • 만약 접근자에서 따로 기능 변경이 필요없다면 super.someProperty라고 반환해주면 됩니다.

프로퍼티 감시자(willSet, didSet) 재정의

프로퍼티 감시자의 재정의는
상수 저장 프로퍼티나 읽기 전용 연산 프로퍼티가 아니라면 (set을 할 수 없기 때문)
조상클래스의 정의한 프로퍼티가 연산 프로퍼티인지 저장 프로퍼티인지 상관없이 재정의가 가능합니다.

프로퍼티 감시자를 재정의 한다고 해서 조상클래스의 프로퍼티 감시자가 작동하지 않는 것은 아닙니다.

프로퍼티 접근자와 프로퍼티 감시자는 동시에 정의할 수 없습니다. 만약 둘 다 동작하길 원한다면 재정의하는 접근자에 프로퍼티 감시자의 역할을 구현해야 합니다.

서브스크립트 재정의

서브스크립트도 매개변수와 반환 타입이 다르면 다른 서브스크립트로 취급하므로, 자식클래서에서 재정의하려는 서브스크립트라면 부모클래스 서브스크립트의 매개변수와 반환 타입이 같아야 합니다.

재정의 방지

재정의를 방지하고 싶다면 방지하고 싶은 특성(프로퍼티, 메서드, 서브스크립트 등) 앞에 final 키워드를 명시하면 됩니다.

자료 출처: 야곰 스위프트 프로그래밍 3판

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
profile
iOS 개발자입니다.

0개의 댓글