저번에 배운 클래스 개념에서 좀 더 심화된 내용인 '상속'에 대한 학습정리를 본 포스팅을 통해 해보고자 한다😎
타 객체지향언어(C++, JAVA) 에서도 배웠어서 익숙한 개념이지만, 처음부터 다시 배운다는 마음가짐으로 하나하나 학습해보겠다👊
특정 클래스로부터 파생되어 새로운 기능을 추가하는 것
상속은 Swift의 다른 타입과 확실하게 구별되는 클래스만의 고유한 특징이다.
클래스가 다른 클래스로부터 상속될 때 상속하는 클래스를 '하위(자식) 클래스(subclass)' 라고 하고 상속된 클래스를 '상위(부모) 클래스(superclass)'라고 한다.
서브클래스는 슈퍼클래스로부터 물려받은 메서드를 호출할 수 있고 프로퍼티에 접근이 가능하다.
또한, 슈퍼클래스로부터 물려받은 메서드, 프로퍼티, 서브스크립트 등을 자신만의 내용으로 재정의할 수도 있다.
클래스의 상속은 다음과 같은 구문을 통해 이뤄진다.
class 클래스 이름 : 부모클래스 이름 {
프로퍼티 & 메서드들
}
예시 코드를 통해 이를 어떻게 활용했는지 더욱 자세히 알아보자.
class Student: Person {
var grade: String = "F"
func study() {
print("Study hard")
}
}
let yagom: Person = Person()
yagom.name = "yagom"
yagom.age = 99
print(yagom.introduction)
yagom.speak()
let jay: Student = Student()
jay.name = "jay"
jay.grade = "A"
print(jay.introduction)
jay.speak()
jay.study()
상속하는 클래스 Student 뒤에 콜론(:)
을 붙이고 그 뒤에 상속된 클래스를 적어줌으로써 상속을 선언해준다.
또한, 서브클래스로 생성한 인스턴스는 부모클래스의 프로퍼티 접근과 메서드 호출이 가능하므로 아래의 코드들은 모두 정상적으로 실행된다.
상위 클래스에서 상속될 특성을 하위 클래스에서 '재정의' 하는 것
여기서 말하는 '특성'이란 인스턴스 메서드, 타입 메서드, 인스턴스 프로퍼티, 타입 프로퍼티, 서브스크립트 등을 말한다.
재정의는 상속받은 특성들의 새로운 정의 앞에 override
라는 키워드를 붙임으로써 선언이 가능하다.
override
키워드는 스위프트 컴파일러가 조상클래스에 해당 특성이 있는지 확인하는 역할을 한다.
만일 조상클래스에 재정의할 해당 특성이 없다면 컴파일 오류가 발생하게 된다.
override
를 활용하여 재정의 구문을 작성해보면 아래와 같다.
override func someMethod() { }
자식클래스에서 재정의가 이뤄졌을때, 부모클래스의 특성을 자식클래스에서 사용하고 싶을 때 사용함
자식클래스의 부모클래스 특성 재정의 과정에서, 부모클래스 특성의 사용이 필요한 경우에 자주 쓰인다.
활용예제를 살펴보면 아래와 같다.
class SavingAccount: BankAccount {
var interestRate: Float = 0.0
func calculateInterest() -> Float {
return interestRate * accountBalance
}
override func displayBalance() {
super.displayBalance()
print("Prevailing interest rate is \(interestRate)")
}
}
displayBalance 메서드를 재정의하는 과정에서 super 키워드를 이용하여 부모클래스(BankAccount)에 있는 displayBalance 메서드를 호출하였음을 확인할 수 있다.
상속된 인스턴스 또는 타입 메서드를 재정의하여 하위 클래스 내에서 용도에 맞도록 재정의할 수 있다.
class Vehicle {
func makeNoise() {
print("gogo")
}
}
class Train: Vehicle {
override func makeNoise() {
print("choo choo")
}
}
let train = Train()
train.makeNoise() //choo choo
Vehicle 클래스를 상속한 Train 이라는 클래스가 생성되었고, 그 과정에서 makeNoise 메서드도 재정의가 이뤄졌다.
따라서 Train 인스턴스를 생성하고 거기서 makeNoise 메서드를 호출한다면, 하위클래스인 Train에서 재정의된 makeNoise 메서드가 호출됨을 확인할 수 있다.
메서드와 마찬가지로 프로퍼티 또한 부모클래스로부터 상속받은 인스턴스 프로퍼티나 타입 프로퍼티를 자식클래스에서 용도에 맞게 재정의할 수 있다.
단, 저장 프로퍼티는 재정의가 불가능하며 재정의가 가능한 것은 접근자(getter), 설정자(setter), 프로퍼티 감시자(property observer)가 있다.
연산 프로퍼티 재정의
설정자/접근자가 모두 존재하는 프로퍼티를 접근자만 존재하게끔 재정의할 수 없다.
하지만, 접근자만 존재했던 프로퍼티에 설정자를 추가 해주거나 접근자의 내용을 수정하는 식의 재정의는 가능하다.
아래의 코드를 통해 연산 프로퍼티 재정의가 실제로 어떻게 이뤄지는지 살펴보자.
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
}
}
}
Person 클래스의 introduction 프로퍼티는 Student 클래스에서 내용이 수정됨으로써 재정의가 이뤄졌음을 확인할 수 있다.
참고로, 연산 프로퍼티가 읽기 전용일 경우에는 get 메서드를 생략하고 return 구문만 집어넣어도 상관이 없다.
Person 클래스의 koreanAge 프로퍼티는 읽기전용 프로퍼티였는데, set 메서드가 추가됨으로써 읽기/쓰기가 모두 가능한 프로퍼티로 재정의 되었음을 확인할 수 있다.
프로퍼티 감시자에 관한 개념은 아직 학습을 하지 않았으므로 추후 별도로 포스팅 하겠다.
이번 포스팅에서는 상속의 기본적인 개념과 사용 예제들을 위주로 살펴보았다.
상속에 관한 더욱 많은 내용들이 존재하지만 아직 추가학습을 하지 않았다.
빠른 시일내에, 추가적인 공부를 진행하여 2편도 작성하겠다 🥹