나의 발자취
클래스의 상속과 재정의 본문
재정의(Overriding)
Overloading vs Overriding
/**==============================================================================
- 오버로딩(overloading) - (엉어 뜻: 과적) 함수에서 함수의 하나의 이름에 여러 함수를 대응시켜서 사용
- 오버라이딩(overriding) - (영어 뜻: 재정의) 클래스의 상속에서 상위클래스의 속성/메서드를 재정의(기능을 약간 변형하여 사용)하는 것
- 재정의
- 서브클래스에서 슈퍼클래스의 동일한 멤버를 변형하여 구현
- 재정의 가능한 대상(멤버)
- 1) 속성 (하지만, 저장 속성에 대한 재정의는 어떠한 경우에도 불가능)
- 2) 메서드 (메서드, 서브스크립트, 생성자)
==> 속성과 메서드는 재정의 방식이 다름 ⭐️
==============================================================================**/
class Aclass {
func doSomething() {
print("Do something")
}
}
class Bclass: Aclass {
override func doSomething() {
super.doSomething()
print("Do another job")
}
}
* 저장속성의 재정의는 원칙적 불가 (데이터 구조의 변형은 불가)
Overriding의 기본 문법
class SomeSuperclass {
// 저장속성
var aValue = 0
// 메서드
func doSomething() {
print("Do something")
}
}
class SomeSubclass: SomeSuperclass {
// 저장속성의 재정의는 원칙적 불가
//override var aValue = 3
// 저장속성 ===> 계산속성으로는 재정의 가능
// 그러나, 메서드 형태로 부수적 추가는 가능
override var aValue: Int {
get {
return 1
}
set { // self로 쓰면 안됨
super.aValue = newValue
}
}
// 메서드는 (어떤 형태로든)재정의 가능 ===> 변형 / 대체
override func doSomething() {
super.doSomething()
print("Do something 2")
//super.doSomething()
}
}
2-1) 재정의 방식
1) 속성의 재정의 (엄격)
/**===============================================================================================
- (타입/인스턴스 속성을 구분해서 생각해야 하지만, 실질적으로 타입 속성을 재정의 하는 것은 드문 일이므로 배제하고 생각하면 됨)
- (1) 저장 속성의 재정의
- 원칙적으로 불가능(고유의 메모리 공간은 유지 해야함)
- ==> 저장 속성은 고유의 메모리 공간이 있으므로 하위클래스에서 고유의 메모리 공간을 바꾸는 방식으로의 재정의는 불가능
- (메서드 형태로 추가하는 방식의 재정의는 가능)
- ==> 읽기/쓰기 가능한 계산속성으로 재정의 가능(메서드) (읽기만 가능한 계산 속성으로 정의 불가능 - 기능 축소는 불가능 X)
- ==> 속성 감시자를 추가는 가능(메서드) (실질적 단순 메서드를 추가해서 저장 속성이 변하는 시점을 관찰할뿐)
- (2) 계산 속성(메서드)의 재정의
- (실질적인 메서드이기 때문에 메서드 형태로의 재정의만 가능. 기능의 범위를 축소하는 형태로의 재정의는 불가능)
- (상위)읽기전용 계산 속성을 ===> (하위)읽기/쓰기가 가능한 가능한 속성으로 재정의 가능(확장 O)
- ===> 속성 감시자를 추가하는 재정의는 불가능 (읽기 전용 속성을 관찰 할 수 없음 - 논리에 안 맞음)
- (상위)읽기/쓰기 계산 속성을 ===> (하위)읽기만 가능한 가능한 속성으로만 재정의 불가능(기능 제한 X)
- ===> 속성 감시자를 추가하는 재정의 가능 (관찰은 가능)
===================================
- 읽기 메서드 ===> 읽기 메서드
- 쓰기 메서드 ===> x (불가능)
- 읽기 메서드 ===> 읽기 메서드
- x ===> 쓰기 메서드 (가능) // 확장은 가능
===================================
- ⭐️ 인스턴스 속성의 대원칙
- 1) 저장 속성 재정의는 원칙적으로 불가능(하위 속성에서 상위 속성의 고유의 메모리 공간을 변형 불가).
메서드 방식(계산 속성으로의 재정의 가능)으로 추가는 가능
- 2) 계산 속성의 유지/확장은 가능, 축소는 불가능
- 3) 속성 감시자(메서드)를 추가하는 재정의는 언제나 가능(실질적 단순 메서드 추가)
(다만, 읽기전용 계산 속성을 관찰하는 것은 의미 없으므로 불가능)
- (실질적으로 드뭄)
- 타입 속성의 재정의 원칙
- 1) 타입 저장 속성은 재정의 불가 - static키워드 (계산속성으로 재정의하거나, 속성 감시자를 추가하는 것도 불가능)
- 2) 타입 계산 속성 - class 키워드인 경우 계산 속성 재정의 (확장방식) 가능.
- 3) 재정의한 타입 저장/계산 속성에는 감시자 추가 원칙적으로 불가
===============================================================================================**/
2) 메서드의 재정의 (메서드, 스크립트, 생성자)- 타입/인스턴스 속성 관계 없음
class Vehicle {
var currentSpeed = 0.0
var halfSpeed: Double {
get {
return currentSpeed / 2
}
set {
currentSpeed = newValue * 2
}
}
}
class Bicycle: Vehicle {
// 저장 속성 추가는 당연히 가능
var hasBasket = false
// 1) 저장속성 ===> ⭐️ 계산속성으로 재정의(메서드 추가) 가능
override var currentSpeed: Double {
// 상위 속성이기 때문에 super키워드 필요
get {
return super.currentSpeed // 1.0
}
set {
super.currentSpeed = newValue
}
}
// 1) 저장속성 ===> ⭐️ 속성감시자를 추가하는 재정의(메서드 추가)는 가능
// override var currentSpeed: Double {
// // 상위 속성이기 때문에 super키워드 필요
// willSet {
// print("값이 \(currentSpeed)에서 \(newValue)로 변경 예정")
// }
// didSet {
// print("값이 \(oldValue)에서 \(currentSpeed)로 변경 예정")
// }
// }
// ⭐️ 계산속성을 재정의 가능 (super키워드 주의)
// override var halfSpeed: Double {
// get {
// return super.currentSpeed / 2
// }
// set {
// super.currentSpeed = newValue * 2
// }
// }
// ⭐️ 계산속성을 재정의 하면서, 속성감시자 추가 가능 (속성감시자 부분 참고)
// override var halfSpeed: Double {
// willSet {
// print("값이 \(halfSpeed)에서 \(newValue)로 변경 예정")
// }
// didSet {
// print("값이 \(oldValue)에서 \(halfSpeed)로 변경 예정")
// }
// }
}
메서드의 재정의 방식
class Vehicle1 {
var currentSpeed = 0.0
var datas = ["1", "2", "3", "4", "5"]
func makeNoise() {
print("경적을 울린다.")
}
subscript(index: Int) -> String {
get {
if index > 4 {
return "0"
}
return datas[index]
}
set {
datas[index] = newValue
}
}
}
// 메서드의 재정의 방식 ⭐️
class Bicycle1: Vehicle1 {
// 1) 상위 => 하위 호출 (가능)
// override func makeNoise() {
// super.makeNoise()
// print("자전거가 지나간다고 소리친다.")
// }
// 2) 하위 => 상위 호출 (가능)
// override func makeNoise() {
// print("자전거가 지나간다고 소리친다.")
// super.makeNoise()
// }
// 3) 상위구현 아예 무시 (가능)
override func makeNoise() {
print("경적을 울리고, 자전거가 지나간다고 소리친다.")
}
// 서브스크립트도 재정의 가능
override subscript(index: Int) -> String {
get {
if index > 4 {
return "777"
}
return super[index]
}
set {
super[index] = newValue
}
}
}
let v = Bicycle1()
v.currentSpeed
v.makeNoise()
//v[0]
2-3) 메모리 구조를 통한 이해
상속과 재정의(Overriding) 그리고 메모리 구조
/**================================================================
- 재정의(overriding) - 현재의 클래스에 맞게 상위 멤버를 변형시키서, 사용 하려는 것
- 대원칙:
- 1) 저장 속성 재정의 불가
- 2) 메서드는 자유롧게 재정의 가능(다만, 기능 확장만 가능 - 기능 축소 불가의 의미)
- (재정의를 하지 않아도, 상속에서는 당연히 모든 멤버의 상속이 일어남)
- 저장 속성은 실제 인스턴스에 각각의 멤버별(속성별로) 저장공간이 있고,
- 변형하는 것이 불가 (상속의 기본 원칙)
- 메서드는 타입(데이터 영역)에만 배열형태로 주소값을 저장되어 존재하므로
- 각 상속단계에서 재정의 되고 대체 되는 것이 당연
==================================================================**/
'앱 개발 > iOS' 카테고리의 다른 글
iOS:: 무선으로 시뮬레이터 빌드하는 법 (1) | 2022.09.20 |
---|---|
초기화의 과정과 생성자 (0) | 2022.07.14 |
클래스의 상속과 초기화(Inheritance, Initialization) (0) | 2022.07.02 |
접근 제어(Access Control) (0) | 2022.07.01 |
서브스크립트 (0) | 2022.06.26 |