오버라이딩

라무·2023년 8월 9일
0

정의

상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정하는 것을 말한다

💡 서브 클래스에서 상속 받는 인스턴스 메서드, 타입 메서드, 인스턴스 프로퍼티, 타입 프로퍼티, 서브스크립트 등을 서브클래스에서 재정의하는 것을 의미한다

타입메서드 혹은 타입 프로퍼티란 특정 타입에서만 사용가능한 메서드와 프로퍼티를 의미한다

일반 프로퍼티나 메서드는 대부분 어디에서나 사용가능핟

특징

  • override키워드 없이 재정의 할 경우 에러가 난다
  • override 키워드가 있을 경우, 컴파일러는 슈퍼클래스에 오버라이딩 선언한 것과 일치하는 정의가 있는지 확인한다 → 오버라이딩 정의가 올바른지 확인하는 과정

메서드 오버라이딩(Method Overriding)

💡 상속받은 인스턴스&타입 메서드를 오버라이딩(재정의)하여 하위클래스 내에서 해당 메서드를 원하는대로 구현할 수 있다

특징

  • 슈퍼클래스의 메서드를 재정의하기 위해서는 override 키워드를 붙여줘야 한다
  • override를 붙여주지 않을 경우 에러 발생
    • 이유 : 이미 상속해준 클래스인 슈퍼 클래스에 동일한 메서드가 있기 때문이다
    • 즉, 한 클래스에 동일한 메소드를 두개 선언할 수 없으니까!!
  • override란 키워드가 붙으면 컴파일러가 해당 정의가 슈퍼 클래스에 있는지 확인하는 작업을 통해 오버라이딩이 맞는지 아닌지 확인하게 된다
    • 슈퍼 클래스에 해당 메소드가 정의되어 있지 않으면 에러가 난다

super.는 뭘까?

💡 슈퍼클래스의 메서드를 실행하는 것
  • 예를 들면 UIViewController클래스를 상속받을 때 override func viewDidLoad()와 super.viewDidLoad()
    • override func viewDidLoad()는 UIViewController의 함수 중 하나인 viewDidLoad를 재정의하겠다는 것을 의미한다
    • super.viewDidLoad()는 UIViewController의 viewDidLoad함수에서 viewDidLoad메서드를 호출한다는 것을 의미한다

메서드 오버라이딩 예시

  • Teacher 클래스는 Human이라는 클래스를 상속받았다 → Human클래스의 메소드와 프로퍼티 사용이 가능하다
  • 재정의 키워드(override)없이 서브클래스에서 슈퍼클래스의 메서드를 재정의할 경우 에러가 발생한다!! → 반드시 재정의 키워드(override) 필요
  • description을 Teacher에서 재정의했는데 Human의 description도 부르고 싶다면? → super를 사용한다
class Human {
    func description() {
        print("나는 사람입니다")
    }
}
 
//오버라이딩 전
class Teacher: Human { //Human 클래스 상속
}

let rayoung: Teacher = .init()
rayoung.description()  //나는 사람입니다

//오버라이딩 후(잘못된 예시) -> description()을 print("나는 선생입니다")로 재정의 시도
class Teacher: Humane {
		func description() {
			print("나는 선생입니다")  //에러
		}
}

//오버라이딩 후(올바른 예시)
class Teacher: Human {
    override func description() {
        print("나는 선생입니다")
    }
}

let rayoung: Teacher = .init()
rayoung.description()  //나는 선생입니다

//super를 사용해서 슈퍼 클래스의 메소드 실행
class Teacher: Human {
    override func description() {
        super.description()
        print("나는 선생입니다")
    }
}
rayoung.description()  //나는 사람입니다. 나는 선생입니다

//잘못된 오버라이딩2 - 슈퍼 클래스에 없는 메소드를 오버라이딩함
class Teacher: Human {
    override func printHello() { }       // Method does not override any method from its superclass
}

프로퍼티 오버라이딩(Property Overriding)

정의

💡 상속받은 프로퍼티를 오버라이딩(재정의)하여 해당 속성에 대한 getter, setter를 제공하거나 상속받은 프로퍼티 값의 변경을 추적할 수 있도록 프로퍼티 옵저버를 추가할 수 있다

프로퍼티 옵저버(WillSet&DidSet)

  • 즉, 프로퍼티를 오버라이딩 한다는 것은 getter, setter를 추가할 수 있다는 것이다 → 이말은 즉, 연산 속성을 추가할 수 있다는 것을 의미한다

특징

  • 서브 클래스에서는 상속된 프로퍼티의 특성이 저장 프로퍼티인지 연산프로퍼티인지 알 수 없고 상속받은 프로퍼티의 이름과 타입정도만 알고 있다
    • 그래서 오버라이딩(재정의)할 경우 슈퍼 클래스에 해당 이름과 타입을 가진 프로퍼티가 있는지 확인해야 하기 때문에 오버라이딩(재정의)시 프로퍼티의 이름과 타입을 반드시 명시해야 한다

저장 프로퍼티

저장 프로퍼티는 그냥 오버라이딩이 될까?

가능하긴 한데,,, 조건이 있다 → get와 set으로

잠깐 🖐🏻! 저장 프로퍼티 오버라이딩이란? → 저장 속성을 추가 or 변경하는 것을 의미한다

class Human {
    var name = "Sodeul"
}
 
class Teacher: Human {
    override var name: String = "Sodeul2" // Cannot override with a stored property 'name'
}

저장 프로퍼티에 서브클래스에서 연산 속성인 getter(get)과 setter(set)을 추가해서 어떻게 오버라이딩할까?

getter와 setter을 모두 사용해서 구현한다

  • getter만 있을 경우 → 안된다!
    • 이유 : 기본적으로 슈퍼클래스에 있는 저장 프로퍼티는 getter, setter가 모두 제공되는 읽기&쓰기가 가능한 프로퍼티인데 그걸 서브클래스에서 getter만 가능하도록 오버라이딩하면 에러가 난다
    • 왜냐하면 원래 get, set다 가능하니까,,,!!!!!!!!!!!
  • 즉, 저장프로퍼티는 getter와 setter를 모두 붙여서 오버라이딩이 가능하다

저장 프로퍼티 + getter&setter예시

class Human {
    var name = "Sodeul"
}

//그냥 오버라이딩 예시 -> 오버라이딩 불가능
class Teacher: Human {
    override var name: String = "Sodeul2"       // Cannot override with a stored property 'name'
}

//getter와 setter모두 있는 오버라이딩 예시 -> 오버라이딩 가능
class Teacher: Human {
    var alias = "SodeulSodeul"
 
    override var name: String {   
        get {
            return self.alias
        }
        set {
            self.alias = newValue
        }
    }
}

연산 프로퍼티

연산 프로퍼티를 오버라이딩해서 getter만 구현해도 될까?

될 수도 안될 수도,,,?

  • 슈퍼클래스에 getter와 setter모두 구현되어 있을 경우
    • 서브 클래스에서 getter만은 구현이 안된다
  • 슈퍼클래스에 getter만 있을 경우
    • 서브 클래스에서 getter만 구현 가능
    • 혹은 getter와 setter모두 구현 가능

연산 프로퍼티 예시

  • 슈퍼 클래스가 getter로만 구성
//슈퍼 클래스가 getter로만 구성
class Human {
    var name = "Sodeul"
 
    var alias: String { //getter
        return self.name + " 바보"
    }
}

//get과 set 구현 가능(즉, 오버라이딩 가능)
class Teacher: Human {
    override var alias: String {
        get {
            return self.name + " 멍청이"
        }
        set {
            self.name = newValue
        }
    }
}
  • 슈퍼클래스가 getter와 setter모두 가질경우
//슈퍼클래스가 getter와 setter모두 가질경우
class Human {
    var name = "Sodeul"
 
    var alias: String {
        get {
            return self.name + " 바보"
        }
        set {
            self.name = newValue
        }
    }
}

//get만 오버라이딩 구현할 경우 -> 에러발생
class Teacher: Human {
    override var alias: String {        // Cannot override mutable property with read-only property 'alias'
       return self.name + " 멍청이"
    }
}

let human: Human = .init() 
human.alais //Sodeul 바보

let teacher: Teacher = .init()
teacher.alais //Sodeul 멍청이

프로퍼티 옵저버 추가하기

💡 저장 프로퍼티의 경우, var로 선언된 프로퍼티만 오버라이딩으로 옵저버를 추가할 수 있다. 연산 프로퍼티의 경우 getter, setter가 모두 구현된 경우에만 오버라이딩으로 옵저버를 추가할수가 있다

저장프로퍼티에 옵저버 추가하기

  • 왜 let일때는 안될까? → 당연히,,,안된다!!
    • 왜냐하면 프로퍼티 옵저버 자체가 값이 변경될 때 값이 변경됬다고 알려주는 것이니까 값을 바꿀 수 없는 let에는 적용이 불가능하다
  • 유의할 점!!!
    • 컴파일러는 서브 클래스에서 오버라이딩(재정의) 된 프로퍼티가 오버라이딩된 게 맞는지 확인하기 위해서 프로퍼티를 오버라이딩 시에는 반드시 프로퍼티 이름과 타입을 명시해야 한다
  • 예시
    • Teacher은 Human클래스를 상속받았다
      - 서브 클래스에서 오버라이딩 하여 프로퍼티 옵저버를 추가할 수 있다

      class Human {
          var name = "Sodeul"
      }
       
      class Teacher: Human {
          override var name: String {
              willSet {
                  print("name 값 변경 된다!!! \(newValue)")
              }
              didSet {
                  print("name 값 변경 됐다!!! \(oldValue)")
              }
          }
      }
      
      let teacher: Teacher = .init()
      teacher.name = "Unknown"

연산 프로퍼티에 옵저버 추가하기

  • 왜 getter만 구현되거나 setter만 구현된 프로퍼티는 옵저버를 추가할 수 없을까? → 너무 당연히 안된다
    • 왜냐하면 getter만 구현된 경우 바로 return 되서 알아서 값이 적용되니까 setter를 써서 값 변경을 할 필요 없고
    • setter만 있을 경우에 값 변경을 적용할 수 없으니까,,, 쓰기만 하고 읽을 수 없으니까
  • 예시
    class Human {
        var name = "Sodeul"
     
        var alias: String {
            get {
                return name + " 바보"
            }
            set {
                self.name = newValue
            }
        }
    }
     
    class Teacher: Human {
        override var alias: String {
            willSet {
                print("연산 프로퍼티 set이 실행되려고 한다!!!!")
            }
            didSet {
                print("연산 프로퍼티 set이 실행 되었다!!!!")
            }
        }
    }
    
    let teacher: Teacher = .init()
    teacher.alias = "Unknown"

final(오버라이딩 금지)

💡 오버라이딩 가능한 프로퍼티, 메소드, 서브스크립트 등에 final을 붙이면 해당 정의는 더이상 오버라이딩(재정의)가 불가능해진다
  • class 앞에 final 키워드를 붙이면 해당 클래스의 상속을 금지하는 것과 같은 의미

주의사항

  • final을 붙이는 것은 오버라이딩을 금지한 것이지 서브클래스에서 접근 자체가 금지된 것은 아니다

예시

class Human {
    final var name = "Sodeul"
    final func description() {
        print("나는 사람입니다")
    }
}

class Teacher: Human {
		override var name: String { //Property overrides a 'final' property
			get { ....

class Teacher: Human {
		override func description() { //Instance method overrides a 'final' instance

//final된 프로퍼티, 메서드 서브클래스에서 접근
let teacher: Teacher = .init()
teacher.name
teacher.description()
profile
ios 개발을 하고있는 라무의 사적인 기술 블로그

0개의 댓글