BOID

[swift] 상속(Inheritance)에대해 뽀시자!!! 본문

swift 시작기

[swift] 상속(Inheritance)에대해 뽀시자!!!

HoonIOS 2021. 5. 7. 12:40

안녕하세요, HoonIOS입니다. :)

오늘은 상속을 뽀시기 위해서 포스팅을 하게 되었는데요,

 상속은 스위프트 가장 큰 특징이자 가장 큰 기능으로서의 하나라고 할 수 있습니다.

 

 Inheritance(상속)

 

상속은 기반 클래스 형태의 클래스가 다른 클래스로 상속을 해주는 클래스 형식으로 클래스 하나에 하나의 클래스만 상속받을 수 있다는 것이 가장 큰 특징입니다.

 

아! 상속은 구조체, 열거형 같은 같타입에서는 사용을 할 수 없으니 값 타입에 상속시킨다고 열심히 코딩을 하시면 말짱 도루묵이 되니 주의하셔야 합니다!

 

클래스는 상속에 따라 부모클래스, 자식 클래스로 나뉘는데 이에 대해 간단하게 설명을 드려보겠습니다.

B클래스가 A클래스를 상속받은 B클래스는 자식 클래스라고 표현을 하고 A는 B의 부모 클래스라고 표현을 합니다.

 

참고사항으로 위해서 말씀드렸던 기반클래스라는것은 아무것도 상속을 받지 않은 클래식한 클래스를 기반 클래스라고 부릅니다.

상속을 받게되면 자식 클래스는 부모 클래스의 메서드나 프로퍼티를 그대로 사용을 할 수도 있고 아니야 그대로 사용하고 싶지 않아 라고 하시면 override를 통해서 재정의를 할 수도 있습니다.

 

 Class Inheritance

 

 extension을 사용하면 수평적으로 호떡 누르듯이 쭈우욱 넓히면서 확장을 하게 됩니다. 그러나 상속을 사용하면 extension과는 다르게 수직으로 아파트처럼 위아래로 쭈우욱 확장을 할 수 있습니다.

 

그럼 상속 형태를 한번 알아 보겠습니다. :)

Class 클래스이름: 부모클래스 이름 {
	프로퍼티, 메서드 정의
}

 

이렇게 클론 구분을 해서 자식클래스에 부모 클래스를 상속시켜주면 됩니다.

 

그럼 간단한 예를 들어서 한번 만들어보겠습니다.

 

Vehicle이라는 기반 클래스가 있다고 하고 안에 프로퍼티와 메서드를 아래와 같이 설정하겠습니다.

 

이제 이 기반 클래스를 부모 클래스로 만들어주기 위해서 Bicycle클래스에 상속을 시켜주겠습니다.

 

* 코드 설명

  • 이제 결과를 보시면 Vehicle이 부모 클래스가 되었고 자식 클래스는 Bicycle이 된 것을 확인할 수 있습니다.

상속이 모두 완료되었으니 Bicycle인 자식 클래스의 인스턴스를 생성해보고 자식 클래스에 정의된 값들도 변경을 해보고 부모 클래스의 프로퍼티 메스드에도 한번 접근을 해보겠습니다.

 

* 코드 설명

  • 상속이 잘되고 부모 클래스의 프로퍼티에 접근해서 값을 변경하는 것도 가능한 것을 확인할 수 있습니다.

근데 여기서 자식 클래스의 bicycle도 누구 클래스의 부모 클래스가 될 수 있습니다.

 

한번 bicycle을 부모 클래스로 만들어보겠습니다.

* 코드 설명

  • 이렇게 되면 이제 exercise는 Bicycle의 자식 클래스가 되었고 Bicycle은 부모 클래스가 된 것을 확인을 할 수가 있습니다.
  • 물론 부모 클래스의 값들까지 모두 접근할 수 있습니다.

 

 Overriding methods

 

※ override(재정의란?)

overriding은 부모 클래스로부터 상속받은 메서드, 프로퍼티, 이니셜 라이저 등등을 그대로 사용하고 있지 않고 새롭게 정의하기 위해서 사용을 할 수 있습니다.

 

재정의를 하기 위해서는 override를 앞에다가 사용해야 합니다. 또 자식 클래스에서 부모 클래스의 프로퍼티를 사용하는데 부모 클래스의 재정의하지 않은 특성을 사용하고 싶을 때 super키워드를 사용하면 됩니다.

 

그냥 이렇게 글만 보면은 이해가 되지 않을 것이니 한번 예시를 들어보겠습니다.

 

앞의 예제에서 부모 클래스인 Bicycle클래스에서 makeNoise메서드를 재정의 하겠습니다.

* 코드 설명

  • makeNoise 함수 안에 새로 재정의 해준 값을 넣어줬습니다.

그다음 super키워드도 한번 사용을 해서 override를 하지 않은 부모 클래스의 makeNoise메서드를 호출하겠습니다.

 

* 코드 설명

  • 아 원래 Vehicle 클래스의 makeNoise메서드는 껍데기만 있고 안에 알맹이는 없었는데 그러면 부모 클래스의 메서드가 호출되었는지 모르잖아요 ㅎㅎ

    그래서 안에 print값을 작성해줬습니다.

이제 override 한 makeNoise와 override 안 한 값을 출력하기 위해서 사용한 ridding을 한번 알아보겠습니다.

 

* 코드 설명

  • makeNoise 메서드를 호출하면 override 한 함수 코드가 발생하고 ridding메서드를 호출하면 super 한 부모 클래스의 메서드가 호출이 되는 것을 확인할 수 있습니다.

이렇게 하나하나 보면 헷갈릴 수 있으니 전체 코드를 아래에 놓겠습니다. :) 

 

어떠신가요? 재밌고 신기하지 않으신가요?

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

 

이제는 프로퍼티 override를 해보겠습니다. 

 

 Overriding Properties

 

프로퍼티도 재정의를 할 수 있습니다. 근데 여기서 말하는 프로퍼티 재정의는 모든 프로퍼티들을 재정의할수 있다는 것이 아닙니다. 저장 프로퍼티는 재정의 할 수 없고 연산 프로퍼티, 옵저버 프로퍼티( getter, setter, property Observer )를 재정의하는 것을 말합니다.

 

그럼 연산 프로퍼티 재정의부터 예제를 통해 구현해보겠습니다.

 

*  코드 설명

  • 2개의 읽기 전용 프로퍼티를 구현했습니다.

이 두 개의 프로퍼티를 부모 클래스에서 override 해주겠습니다.

 

* 코드 설명

  • description 프로퍼티는 읽기 전용 프로퍼티에서 읽어올 때의 값만 바꿔서 다시 재정의를 해줬습니다.
  • 이 코드에서는 speed 프로퍼티를 눈여겨보실 필요가 있습니다.

    그 이유는 부모 클래스에서는 Speed 프로퍼티가 읽기 전용 프로퍼티였지만, 자식 클래스에서 override를 통해 speed 프로퍼티를 읽기 쓰기 모두 가능한 프로퍼티로 재정의를 해줬기 때문이죠!

단 연산 프로퍼티를 맘대로 바꾸면 에러가 발생하는 예외상황이 발생합니다.

 

음... 예를 들어 부모 클래스에서 읽기 쓰기 전용 프로퍼티였는데 자식 클래스에서 읽기 전용으로 override를 해버리면 에러가 발생을 합니다.

 

읽기 전용 -> 읽기, 쓰기 전용  ||  읽기 전용 -> 읽기 전용 || 읽기, 쓰기 전용 -> 읽기, 쓰기 전용은 되지만 읽기, 쓰기 -> 쓰기는 에러가 발생을 하게 됩니다.

 

위에 있는 Vehicle의 speed 읽기, 쓰기 전용 프로퍼티를 읽기 전용 프로퍼티로 재정의하면 어떻게 되는지 확인해보겠습니다.

 

우선 speed 연산 프로퍼티는 아래와 같이 정의를 했습니다.

 

* 코드 설명

  • 부모 클래스에서 speed프로퍼티를 읽기 쓰기 전용 연산 프로퍼티로 정의를 했습니다.

이제 이 프로퍼티를 자식 클래스에서 읽기 전용 연산 프로퍼티로 override 하겠습니다.

 

* 코드 설명

  • 그럼 결과물을 보시는 것처럼 에러가 발생하는 것을 알 수 있습니다. 에러 내용을 보니 읽기 쓰기 전용 프로퍼티인 speed를 읽기 전용으로 변할 수 있는 프로퍼티로 재정의 할수 없다는 거이네요 
  • 이렇게 잃기 쓰기 프로퍼티를 읽기 전용으로 override를 하면 에러가 발생을 합니다.

 

이제 연산 프로퍼티에 대해 알아봤으니 프로퍼티 감시자인 옵저버 재정의에 대해 알아보겠습니다.

 

프로퍼티 감시자도 상속 프로퍼티를 통해 재정의를 할 수 있습니다.

 

다만 여기서 불가능한 경우가 있는데 상속 저장 프로퍼 티거나 읽기 전용 연산 프로퍼티는 프로퍼티 감시자를 재정의 할 수 없습니다.

 

그 이유는 무엇일까요? 상수 저장 프로퍼티는 내부 값을 다시 set 할 수 없고 읽기 전용 연산 프로퍼티도 내부 값을 다시 set을 할 수가 없어 willSet, didSet메서드를 사용할 수 없기 때문입니다.

 

프로퍼티 접근자, 프로퍼티 감시자를 한 번에 재정의를 할 수가 없어 만약에 재정의를 하고 싶으면 프로퍼티 감시자의 역할을 구현해줘야 합니다.

 

그렇지만 참고할 것은 프로퍼티 감시자를 재정의하면 조상 클래스에 정의한 프로퍼티 감시자도 동작을 안 하는 게 아니고 같이 동작을 한다는 것입니다.

 

한번 프로퍼티 옵저버의 재정의 예제를 한번 살펴보겠습니다.

 

* 코드 설명

  • 왼쪽의 코드가 부모 클래스에서의 ridePeople 변수에 didSet옵저버를 설정을 해줬고

    그다음으로 오른쪽이 자식 클래스에서 ridePeople 변수에 didSet, WillSet 옵저버 설정으로 재정의해줬습니다.

이제 이것을 설정해줬으니 호출을 하면 어떤 결과가 나올까요?

 

자식 클래스의 옵저버 프로퍼티의 코드 값들만 출려되어야 될 거 같다고 생각을 다들 하시지만 틀렸습니다. 부모 클래스, 자식 클래스 둘 다 옵저버 프로퍼티에 해당하는 순간의 값들이 나오게 됩니다.

 

한번 Bicycle 클래스의 인스턴스를 생성하고 ridePeople 프로퍼티에 값을 넣어보겠습니다.

 

* 코드 설명

  • 해당 클래스의 프로퍼티 값이 바뀌면서 부모 클래스의 didSet이 호출되었고 자식 클래스의 didSet이 호출된 것을 볼 수 있습니다.

    이렇게 부모 클래스, 자식 클래스의 didSet이 모두 호출된 것을 확인할 수 있었습니다. 2개의 didSet 프로퍼티가 호출이 되게 된 것이죠

    ( 근데 여기서 부모 클래스의 didSet이 2번이 호출되네요...ㅠㅠ 이건 저도 잘 모르겠습니다... 흑... 혹시 아시는 분 계시면 댓글 부탁드립니다...ㅠ)

그다음으로 프로퍼티의 읽기, 쓰기 전용 연산 프로퍼티인 speed를 override 해줘 옵저버 프로퍼티를 정의해주겠습니다.

 

* 코드 설명

  • 이렇게 읽기 쓰기 전용 연산 프로퍼티를 override 해서 옵저버 프로퍼티로 재정의를 해줄 수 했습니다.

한번 출력을 해보겠습니다.

 

* 코드 설명

  • 결과를 보면 값을 설정해주면서 부모 클래스의 set에 접근을 하게 되어 값을 return 해주고 새로운 값을 넣어줬기 때문에 값을 set 해줍니다.
  • 값이 바뀌고 났으니 바뀌고 나서 호출하는 옵저버 메서드인 didSet이 동작을 하는 것을 확인할 수 있습니다.

앞에서 말씀드린 것처럼 옵저버 프로퍼티도 override가 안 되는 경우가 있다고 말씀을 드렸는데요

부모 클래스에서 프로퍼티가 읽기 전용으로 구현되어 있다면 자식 클래스로 didSet을 해줘도 구현을 할 수가 없습니다.

 

* 코드 설명

  • 부모 클래스에서 읽기 전용 프로퍼티로 선언된 연산 프로퍼티입니다.

이 프로퍼티를 이제 자식 클래스에서 재정의를 한번 해보겠습니다.

 

* 코드 설명

  • 부모 클래스에서 연산 프로퍼티로 정의가 되어있어 자식 클래스에서 didSet을 구현할 수가 없습니다.

 

 Overriding Subscript

 

서브 스크립트도 함수 형태와 같이  매개변수와 변환 타입이 다르면 다른 서브 스크립트로 취급을 하고 있어 만약 재정의를 하려면 부모 클래스의 서브 스크립트의 서브 스크립트와 같은 매개변수와 반환 타입이 같아야 합니다.

 

한번 예제를 통해 확인해보겠습니다.

 

ridding 기저 클래스에서 객체  클래스를 담을 배열을 선언하고 subscript를 해당 클래스에 구현을 해줬습니다.

 

 

이제 ridding 클래스를 부모 클래스로 만들어주기 위해서 ridding클래스를 상속을 시키고 ridding클래스에 선언해주었던 subscript를 자식 클래스에서 재정의를 해주겠습니다.

 

* 코드 설명

  • subscript를 override 해준 것을 확인을 할 수 있는데요 위 예시같이 재정의를 해주러면 매개변수 타입, 리턴 타입이 부모 클래스의 subscript와 같아야 부모 클래스의 subscript를 재정의할 수 있게 되는 것입니다.

이제 결과를 확인해봐야겠죠!

* 코드 설명

  • exercises클래스 배열 안에 Bicycle클래스를 넣고 해당 인덱스를 출력해보면 override 된 스크립트가 잘 출력이 되는 것을 확인할 수 있습니다. :)

그럼 만약 재정의를 방지를 할 수는 없을까?라는 생각을 가지 실수 있습니다. 정답을 먼저 알려드리면 방지할 수 있습니다.

 

재정의 방지에 대해 알아보겠습니다. :)

 

 preventing Overrides

만약에 변수, 함수, 클래스, 서브 스크립트와 같은 특성들을 재정의해주고 싶지 않으면 final키워드를 앞에 붙여주면 됩니다.

 

final 키워드가 있는 특성들을 재정의해주려면 컴파일 오류가 발생을 해버립니다....

 

간단하게 예시를 구현해봤습니다.

 

* 코드 설명

  • 이렇게 final키워드를 사용해서 상속을 막을 수도 있습니다. :)

 이니셜 라이저 상속 및 재정의

 

부모 클래스의 이니셜 라이저와 같은 이니셜 라이저를 자식 클래스에서 재정의를 해주고 싶으면 override를 하면 됩니다.

 

자식 클래스의 편의 이니셜 라이저가 부모 클래스의 지정 이니셜 라이저를 재정의하는 것도 마찬가지로 override를 해주면 됩니다.

 

하지만 자식 클래스의 이니셜 라이저를 부모 클래스에 재정의를 할 수는 없습니다. 당연한 거지만 자식 클래스에서 부모 클래스의 이니셜 라이저를 호출할 수 없기때문에 재정의를 할수 없습니다.

 

한번 예시를 들어보겠습니다.

 

* 코드 설명

  • 이 클래스는 부모 클래스로 지정 이니셜 라이저를 정의해줬습니다. 
  • convenience는 편의 이니셜 라이저로 사용자 편의에 따라 초기화할 수 있는 것으로 최종적으로 지정 이니셜 라이저를 호출해주면 됩니다.

* 코드 설명

  • 이 클래스는 자식 클래스로 부모 클래스의 이니셜 라이저를 override를 통해 재정의하고 자식 클래스는 부모 클래스를 호출해줘야 하므로 super.init()을 통해 부모 클래스의 이니셜 라이저를 호출해준것을 볼수 있습니다

 

이니셜라이저를 override 할 때 반드시 해당 이니셜 라이저는 구현해줘 라고 요구를 할수 있습니다. 

 

 required init

 

required수식어를 이니셜라이저 앞에 명시하면 이 클래스를 상속받은 자식 클래스는 해당 이니셜 라이저를 반드시 구현해줘야 합니다.

 

쉽게 말해 재정의를 꼭 해야 하는 이니셜 라이저 앞에 required를 붙여준다고 생각을 하시면 됩니다. :) 대신 부모 클래스의 required init을 자식 클래스에서 구현해줄 때는 required 만 붙여주면 됩니다.

 

 

* 코드 설명

  • 부모 클래스에 required init을 구현해줬으므로 자식 클래스에서도 구현을 해줘야 합니다.
  • 만약 자식 클래스에 더 이상 초기화해줄 프로퍼티가 없으면 required init을 자식 클래스에서 구현을 해줄 필요가 없습니다. 자동 상속이 되는 거죠 ㅎㅎ

    아래위의 코드와 같이 자식 클래스에서 더 이상 초기화해줄 것이 없으니 이니셜 라이저를 또 구현해줄 필요가 없습니다. 따라서 required init은 자식 클래스에 있는 것이 부모 클래스에 자동 상속되게 됩니다.

 

특별한 경우도 있습니다. 부모 클래스에서 일반 이니셜 라이저였는데 부모 클래스에서 요구 이니셜 라이저로 변환을 할 수도 있습니다. 이럴 때는 required override를 통해 일반 이니셜 라이저를 요구 이니셜 라이저로 구현을 해주면 됩니다.

 

지정 이니셜 라이저뿐만 아니라 편의 이니셜 라이저도 변경해줄 수 있는데 이럴 때 required convienience를 해주면 요구 편의 이니셜 라이저로 바뀌게 됩니다.

 

* 코드 설명

  • person 조상 클래스에서는 기본 이니셜 라이저로 설정을 했지만 student 자식 클래스에서는 요구 지정 이니셜 라이저로 override해 다음 상속부터는 무조건 요구를 해야되는 이니셜라이저로 변경이 되었습니다.
  • UniversityStudent 부모 클래스인 Student는 요구 편의 이니셜 라이저를 설정해줘서 해당 자식 클래스부터는 편의 이니셜 라이저를 요구하게 변경이 되었습니다.

이렇게 꼭 필요한 이니셜 라이저는 required를 통해서 설정을 해줄 수 있습니다. :)

 

 

이렇게 상속에 대해 알아봤는데요 적다 보니깐 정말 기네요....ㅎㅎ

스위프트의 특징 중인 하나인 상속은 매우 중요하니깐 꼭 읽어보시길 바라겠습니다. 

 

그리고 이 글 작성을 한 게 어버이날 전날인데요 내일은 친구 결혼식이 있어 지방에 가봐야 돼서 오늘 부모님, 할머니에게 감사하다고 꽃을 사러 가려고 합니다.

 

이거 글을 읽어주신 독자분께서도 부모님께 감사하다고 말씀드린 것은 어떨까요 ㅎㅎ

 

긴 글 읽어주셔서 감사합니다. :)

반응형
Comments