본문 바로가기
Swift

오버라이딩(Overriding)에 관하여

by iOS 개린이 2023. 1. 26.

오버라이딩(Overriding)

-공식문서에 따르면 오버라이딩은 "하위 클래스가 슈퍼클래스로부터 상속되는 인스턴스 메서드, 타입 메서드, 인스턴스 프로퍼티, 타입 프로퍼티, 서브스크립트 등이 사용자 지정 구현이 가능한데 이것을 오버라이딩이라고 부른다."

( 쉽게 말해 "재정의")

 

-서브클래스가 슈퍼클래스로부터 상속받은 메서드나 프로퍼티 등을 재정의 해주는 키워드로

사용 방법은 정의하기 전에 "override" 키워드를 붙여주는 것이다.

만약 슈퍼 클래스에 재정의해줄 대상이 없는데 override 키워드를 붙이면 컴파일 에러가 발생한다.

 

-서브클래스에서 슈퍼클래스의 특성(메서드, 프로퍼티 등)을 재정의 했을 때, 슈퍼클래스의 특성도 같이 서브클래스에서 활용하고 싶다면 "super" 프로퍼티를 사용하면 된다.

 

우리가 UIViewController를 상속받는 클래스를 만들었을 때, 항상 사용하는 viewDidLoad 메서드 안에 "super" 프로퍼티를 사용하는 것을 예로 볼 수 있겠죠?

 

super.viewDidLoad 메서드를 통해서 슈퍼클래스에 있는 특성을 활용하는 것 입니다.

 

 

 

메서드 오버라이딩

 

class Person {
    var name : String = "철수"

    func introduction() {
        print("나의 이름은 \(name) 입니다.")
    }
}
 
class Student: Person {

    var nickname : String = "마마보이"

    override func introduction() {
       
        print("나의 별명은 \(nickname) 입니다.")
    }
}

let chulsu : Person = Person()
chulsu.introduction()          //"나의 이름은 철수 입니다."

let chulsu : Student = Student()
chulsu.introduction()         //"나의 별명은 마마보이 입니다."

 

chulsu라는 Person의 인스턴스를 생성하여 introduction 메소드를 사용하면 주석과 같이 Person 클래스에 있는 메소드가 실행되고, 이것을 재정의 한 Human 클래스의 introduction 메소드는 내가 지정해놓은 코드가 실행된다.

 

만약 "나의 이름은 철수입니다"와 "나의 별명은 마마보이 입니다"를 같이 사용하고 싶다면? 

 

override func introduction() {
   super.introduction()
   print("나의 별명은 \(nickname) 입니다.")
}

let chulsu : Student = Student()
chulsu.introduction()         //"나의 이름은 철수 입니다."  "나의 별명은 마마보이 입니다."

오버라이딩 메소드 안에 super 프로퍼티를 사용하여 introduction 메소드를 선언해주면 된다.

 

 

 

프로퍼티 오버라이딩

-메서드 오버라이딩과 마찬가지로 슈퍼클래스로부터 상속받은 프로퍼티를 서브클래스에서 용도에 맞게 재정의할 수 있다.

 

1. 프로퍼티에도 연산 프로퍼티, 타입 프로퍼티, 저장 프로퍼티 종류가 있는데, 프로퍼티 오버라이딩에서 저장 프로퍼티는 재정의가 불가능하다. (또한 슈퍼클래스에 있는 프로퍼티의 종류와 이름이 일치해야 오버라이딩이 가능하다.)

 

따라서 프로퍼티를 재정의한다는 것은 프로퍼티 자체를 재정의하는 것이 아니라 프로퍼티의 getter, setter, observe 등을 재정의하는 것이다.

 

2. 슈퍼클래스에서 getter(읽기)가 가능한 프로퍼티였을 때 서브클래스에서 getter/setter(읽기/쓰기)가 모두 가능한 프로퍼티로 재정의가 가능하다.

하지만 getter/setter(읽기/쓰기)가 모두 가능한 프로퍼티를 서브클래스에서 읽기 전용으로만 오버라이딩하는 것은 불가능

 

3. 프로퍼티 옵저버를 추가해주는 재정의도 가능하다. 슈퍼클래스에서 정의한 프로퍼티가 저장 프로퍼티인지 연산 프로퍼티인지와 상관없이 재정의 가능하다.

하지만 상수로 선언된 저장 프로퍼티의 경우 그리고 getter 전용 프로퍼티의 경우에는 옵저버를 추가할 수 없다. 

(원천적으로 상수나 읽기 전용은 값 설정이 불가능하기 때문에 프로퍼티 옵저버 추가 또한 불가능.)

 

1. 저장 프로퍼티는 재정의가 불가능함의 예

class Person {
    var name : String = "철수"
}
 
class Student: Person {
    override var name : String = "영수"     //에러발생! 저장프로퍼티 오버라이딩 불가
}

 

저장 프로퍼티인 name을 오버라이딩하는 것은 불가하다.

 

 

 

2-1. 저장 프로퍼티에 연산속성인 getter(읽기)를 추가한 재정의는?

class Person {
    var name : String = "철수"
}
 
class Student: Person {
    var alias = "영희"

    override var name : String {      //에러발생!
       return self.alias
    } 
}

 

마찬가지로 에러가 발생한다. 저장 프로퍼티는 기본적으로 getter/setter가 모두 가능한 프로퍼티이다.

하지만 name을 getter 전용으로만 재정의해주려고 하니 에러가 발생하는 것이다.

 

해결책은 슈퍼클래스에 정의된 것과 같이 getter/setter를 모두 구현해주는 오버라이딩을  하는 것!

 

 

2-2. 연산 프로퍼티에서 연산속성을 빼주면?

 

class Person {
   var name : String = "철수"
    
   var alias : String {
      get {
          return self.name + "마마보이"
      }
      set {
          self.name = newValue
      }
   }
}
 
class Student: Person {
    override var alias : String {          //에러발생!
       return self.name + "파파보이"
    } 
}

 

슈퍼클래스에 있는 alias 라는 get/set 전용 연산 프로퍼티를

서브클래스에서 get 전용 연산 프로퍼티로 set 속성을 빼주는 오버라이딩을 하니 에러가 발생하는 것.  

 

따라서 슈퍼클래스에 있는 get/set 전용 연산 프로퍼티의 속성을 get전용으로 변경해주는 것은 불가능하지만

반대로 get전용을 get/set 전용 연산 프로퍼티로 오버라이딩해주는 것은 가능하다.

 

 

 

3. 옵저버 프로퍼티 추가 재정의

 

저장 프로퍼티에서 프로퍼티 옵저버 재정의

class Person {
   var name : String = "철수"
}
 
class Student: Person {
   override var name : String {
      willSet {
          print("내 이름 변경되기 직전이다. 변경 후 이름은 \(newValue)")
      }
      didSet {
          print("내 이름 변경되었다. 변경 전 이름은 \(oldValue)")
      }
   }
}

 

위의 결과는 문제없이 잘 작동한다. 이렇게 슈퍼클래스의 저장 프로퍼티는 프로퍼티 옵저버 추가 재정의가 가능하다.

슈퍼클래스에서 변수 name이 만약 상수 name이었다면? -> 프로퍼티 옵저버 오버라이딩 불가능

 

또한 슈퍼클래스의 name에 타입을 명시하지 않고 타입 추론으로 선언해도, 

서브클래스에서 오버라이딩 할 경우에는 타입을 꼭 명시해주어야 한다.

(컴파일러가 name이란 프로퍼티를 오버라이딩 하는 것이 맞는 것인지 "프로퍼티 명" 과 "프로퍼티 타입"을 체크하기 위해 )

 

 

연산 프로퍼티에서 프로퍼티 옵저버 재정의 

 

class Person {
   var name : String = "철수"
   
   var alias : String {
      get {
          return self.name + "마마보이"
      }set {
          self.name = newValue
      }
   }
}
 
class Student: Person {
   override var alias : String {
      willSet {
          print("연산 프로퍼티 set이 실행되기 전")
      }
      didSet {
          print("연산 프로퍼티 set이 실행되고 난 후")
      }
   }
}

 

이렇게 get/set 전용인 프로퍼티 alias에 프로퍼티 옵저버 재정의가 가능하다.

만약 alias가 get전용이라면 재정의가 불가능하다.

 

추가

1. 프로퍼티 옵저버를 재정의 해도 슈퍼클래스에서 정의한 프로퍼티 옵저버도 동작함.

2. 이전에 배웠던 "class의 성능향상" 에서 final 이란 키워드에 대해 알아보았는데

프로퍼티나 메서드에 final 키워드를 붙여주면 오버라이딩이 불 가능해져서 class의 성능이 올라간다고 했다.

따라서 final 붙은 것들은 오버라이딩 불 가능

 

 

 

 

 

 

 

Reference

 

- https://babbab2.tistory.com/126

- https://velog.io/@wook4506/iOS-Swift-Swift-%EB%AC%B8%EB%B2%95%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90-19%ED%8E%B8-%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%93%9C-override

-야곰님의 스위프트 프로그래밍 3판

 

 

'Swift' 카테고리의 다른 글

오버로딩(Overloading)에 관하여  (0) 2023.01.30
서브스크립트에 관하여  (0) 2023.01.28
제네릭(Generic)에 관하여  (0) 2022.11.23
Protocol에 관하여  (0) 2022.11.14
lazy variables에 관하여  (0) 2022.10.07