본문 바로가기

안드로이드/[Kotlin]

[Kotlin] 클래스의 상속(Inheritance)과 오버라이딩(Overriding)

코틀린의 모든 클래스에는 공통의 superclass인 Any 가 있으며, 이는 수퍼타입이 선언되지 않은 클래스의 기본 superclass이다. 기본적으로 코틀린의 클래스와 메소드는 기본적으로 final 이여서 상속이 불가능하다. 클래스를 상속 가능하게 하려면 open 키워드를 사용해야 한다.

 

상속을 함으로써 아래와 같은 효과(?)를 얻을 수 있다.

  • 이미 존재하는 클래스를 확장하여 새로운 속성이나 함수를 확장할때
  • 여러 클래스들의 공통점들을 뽑아서 코드관리를 편하게 하고자 할때

 

open class Base(p: Int)

class Derived(p: Int) : Base(p)

위 코드처럼 서브 클래스주 생성자(Primary Constructor)가 있는 경우 주 생성자의 매개 변수를 사용하여 수퍼 클래스를 바로 초기화할 수 있고, 반드시 초기화해야 한다.

 

또한 클래스 상속에는 여러 규칙들이 있다.

  1. 서브클래스는 수퍼 클래스에 존재하는 속성과 '같은 이름' 의 속성을 가질 수 없다.
  2. 서브클래스가 생성 될때는 반드시 수퍼클래스의 생성자까지 호출 되어야 함.
  3. 기본적으로 수퍼클래스에 있는 함수와 같은 이름과 형태를 가진 함수는 서브클래스에서 사용 불가. 하지만 수퍼클래스에서 open만 한다면 여러개의 서브클래스에서 override를 통해서 사용 가능!!

 

아래의 예시 코드를 살펴보자.

fun main() {
   var a = Animal("숭이", 14, "강아지")
   var b = Dog("초코", 16,100)
   var c = Cat("냥냥", 12)
   
   a.introduce()
   b.introduce()
   b.bark()
   c.introduce()
   c.meow()
}
open class Animal (var name: String, var age: Int, var type: String) {
    fun introduce() {
        println("안녕 나는 ${type}인 ${name}이고 ${age}살이야")
    }
}

class Dog (name: String, age: Int, var height: Int) : Animal(name, age, "강아지") {
    fun bark() {
        println("멍멍")
        println("내 키는 ${height}")
    }
}
class Cat (name: String, age: Int) : Animal(name, age, "고양이") {
    fun meow() {
        println("야옹야옹")
    }
}

특이한 점은 Dog 클래스 생성자의 var height :  Int <--이 부분이다. 수퍼 클래스에는 없는 독자적인 속성을 가진다. 겹치는 속성이 수퍼 클래스에 없으므로 사용 가능하다. 그리고 서브 클래스를 생성할 때에는 반드시 수퍼클래스의 생성자를 호출 해 줘야한다. 매우매우 중요하다!!

 

 

 

다음으로는 수퍼 클래스의 메소드와 같은 이름의 메소드를 서브 클래스에서 그 기능을 변경해서 사용하는 방법을 알아보자. override 키워드를 사용한다. 오버라이딩은 이미 수퍼 클래스에서 구현이 끝난 함수의 기능을 서브 클래스에서 변경하고자 할때 사용한다.

fun main() {
  var t = Tiger()
  t.eat()
}

open class Animal {
    open fun eat() {
        println("음식을 먹습니다")
    }
}

class Tiger : Animal() {
    override fun eat() {
    	super.eat()
        println("고기를 먹습니다")
    }
}

수퍼 클래스인 Animal 에서 이미 정의된 eat 메소드를 서브 클래스인 Tiger 에서 override를 통해서 변경하였다. Tiger 클래스의 인스턴스인 t에서  t.eat() 을 통해서 eat 메소드를 호출하면 override된 서브 클래스의 메소드가 호출된다. 부모 클래스의 메소드를 호출하고자 할 때에는 super.eat() 을 사용한다.

 

 

 

 

override 키워드가 붙은 메소드는 open 함수로 간주되므로 서브 클래스에서 override 할 수 있다. 만약 re-override를 금지하고 싶다면 final 키워드를 붙이면 된다. 아래 예시 코드와 같다.

open class Rectangle() : Shape() {
    final override fun draw() { /*...*/ }
}

여기서 오해하기 쉬운 점이 있다. draw 함수에 final 키워드가 붙었다고 해서 더 이상 모든 클래스에서 draw 함수를 override 하지 못한다는 것은 아니다. 가령 Shape 클래스의 서브 클래스에 Rectangle 말고도 Triangle이 있다고 한다면, Triangle 함수에서는 draw 함수를 override 할 수 있다.     

 

Rectangle 클래스 내부의 draw 함수에 final 키워드가 붙음으로써 Rectangle 클래스를 수퍼 클래스로 가지는 서브 클래스에서 draw 함수를 re-override 하지 못하게 된다.