본문 바로가기

Kotlin

Kotlin - 상속 (Inheritance)

Java의 모든 클래스가 Object의 자식 클래스 이듯이 Kotlin 의 모든 클래스는 Any 의 자식클래스가 된다. Any 클래스는 equals(), hasCode(), toString() 함수를 가지고 있다. 그래서 이 함수들은 Kotlin의 모든 클래스에 존재 한다.

그리고 Kotlin의 모든 클래스는 final로 선언 되기 때문에 상속이 불가능 하다. 혹시 이후 확장을 위해 상속 가능하도록 클래스를 선언 하고 싶다면 open 키워드를 사용해야 한다.

open class Base // Class is open for inheritance

명시적으로 부모 클래스를 선언하기 위해서는 class header 부분에 부모 클래스를 작성 하면 된다.

open class Base(p: Int)

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

부모클래스에 생성자(primary, secondary constructor)가 존재 하는 경우 자식클래스의 생성자 들은 부모 클래스의 생성자를 호출하게 된다. 따라서 부모클래스의 생성자를 실행하기 위해 파라미터가 필요하다면 자식 클래스에서도 해당 생성자를 받아서 부모클래스의 생성자를 실행해 줘야 한다. 

// 부모 클래스의 생성자에 파라미터가 필요하므로
// 자식 클래스의 생성자에서는 부모클래스에서 필요한 매개변수를 선언해야 한다.
open class Base(p: Int) {
    init {
        println("Base class init block $p")
    }
}


// 주 생성자 (primary constructor)
// 주 생성자에서는 부모 클래스 명에 괄호("()") 를 사용한다.
class Derived(p: Int) : Base(p) {
    init {
        println("Derived class init block $p")
    }
}

// 보조 생성자 (secondary constructor)
// 보조 생성자가 선언되는 경우 class header의 부모클래스 명에 괄호는 생략가능하며
// 보조 생성자 뒤에 super 키워드를 이용해 부모 클래스의 생성자와 통합한다.
class Derived2 : Base {
    constructor(p: Int) : super(p) {
        println("Derived2 class init block $p")
    }
}

fun main() {
    val a = Derived(10)
//    Base class init block 10
//    Derived class init block 10

    val b = Derived2(5)
//    Base class init block 5
//    Derived2 class init block 5
}

 

함수 오버라이딩 (Overriding methods)

Kotlin에서 함수 오버라이딩을 하려면 명시적으로 override 라고 작성 해야 한다. 만약 부모 클래스에서 open 으로 선언되지 않은 함수를 오버라이딩 하거나, open 으로 선언된 함수를 오버라이딩 하면서 overrid 키워들 누락시키면 컴파일 오류가 발생한다.

오버라이딩 한 함수를 다시 오버라이딩 하지 못하게 하기 위해서는 final 키워드를 이용해서 함수의 재 정의를 막을 수 있다.

open class OverrideBase {
    fun u() {
        println("base final")
    }

    open fun v() {
        println("base open")
    }

    open fun v2() {
        println("base open2")
    }
}

open class OverrideDerived : OverrideBase() {
//    compile 오류 발생
    override fun u() {
        println("derived final")
    }
    override fun v() {
        println("derived override")
    }

//    OverrideDerived 를 상속하는 클래스에서는 v2() 를 override 하지 못하도록 한다.
    final override fun v2() {
        println("derived override2")
    }
}

class OverrideDerived2 : OverrideDerived() {
    override fun v() {
        println("derived2 override")
    }

//    컴파일 에러 - final 로 선언되어 있어서 override 불가
    override fun v2() {
        println("derived2 override2")
    }
}

 

속성 오버라이딩 (Overriding properties)

속성 오버라이딩이란 부모클래스에서 선언된 속성값을 자식클래스에서 다시 선언 하는 것을 말하고, 함수에서와 마찬가지로 부모 클래스에서는 open 키워드를 자식 클래스에서는 override 키워드를 통해 명확히 재 선언 될 수 있고, 재 선언 되었음을 표시 해야한다.

val 로 선언된 속성을 재 정의 하면서 var로 변경 가능하다.

interface Shape {
    val vertexCount: Int
}

class Rectangle(override val vertexCount: Int = 4) : Shape // Always has 4 vertices

class Polygon : Shape {
    override var vertexCount: Int = 0  // Can be set to any number later
}

 

자식클래스 초기화 순서

위(생성자 관련 내용)에서 확인 바와 같이 자식클래스를 생성할때는 부모의 생성자, init 블록이 먼저 실행되고 이후에 자식 클래스의 생성자나 init 블록이 실행된다.

 

부모 클래스 호출하기

부모클래스를 나타내는 super 키워드를 이용하여 부모클래스의 함수나 속성값에 접근 할 수 있다. 내부 클래스(inner class)에서 외부 클래스의 부모클래스에 접근하고 싶은경우에는 super@{outerclass} 형태로 작성해서 outerclass의 부모 클래스에 접근 할 수 있다.

open class SuperClassDemoBase {
    val v get() = "SuperClassDemoBase value"
    open fun f() {
        println("SuperClassDemoBase f()")
    }
}

class SuperClassDemoDerived : SuperClassDemoBase() {
    override fun f() {
        super.f()
        println("SuperClassDemoDerived f()")
    }

    val v2 get() = "SuperClassDemoDerived value ${super.v}"

    inner class SuperClassDemoDerivedInner {
        fun callF() {
            super@SuperClassDemoDerived.f() // SuperClassDemoDerived 클래스의 부모 클래스로 접근한다.
            f() // SuperClassDemoDerived 클래스의 f() 를 실행한다.
            println("SuperClassDemoDerivedInner callF()")
        }
    }
}


fun main() {
    val s = SuperClassDemoDerived()
    s.f()
//    SuperClassDemoBase f()
//    SuperClassDemoDerived f()

    println(s.v2)
//    SuperClassDemoDerived value SuperClassDemoBase value
    
    
    val i = s.SuperClassDemoDerivedInner()
    i.callF()
//    SuperClassDemoBase f()
//    SuperClassDemoBase f()
//    SuperClassDemoDerived f()
//    SuperClassDemoDerivedInner callF()
}

 

오버라이딩 규칙 (Overriding rules)

같은 멤버를 가지고 있는 두 상위클래스를 상속하는 경우에, 자식 클래스에서 중첩되는 해당 멤버를 오버라이드 해야 한다. 그렇지 않으면 컴파일 에러가 발생한다.

그리고 상위클래스의 중첩 멤버에 접근 하고 싶은 경우 super<클래스명> 으로 어떤 상위 클래스에 접근하고 싶은지를 명확히 작성 해야 한다.

open class Rectangle {
    open fun draw() {
        println("Drawing a rectangle")
    }
}

interface Polygon {
    fun draw() {
        println("Drawing a polygon")
    }
}

class Square : Rectangle(), Polygon {
    // draw()는 Rectangle, Polygon양쪽에 있기때문에 override 하지 않으면 컴파일 에러 발생
    override fun draw() {
        // 어떤 상위 클래스의 draw() 를 호출할지 지정해야 한다.
        super<Rectangle>.draw()
        super<Polygon>.draw()
    }
}

 

반응형

'Kotlin' 카테고리의 다른 글

Kotlin - 인터페이스 (Interface)  (0) 2024.01.24
Kotlin - 속성 (Properties)  (0) 2024.01.20
Kotlin - Packages and imports  (0) 2024.01.13
Kotlin - Exceptions  (0) 2024.01.12
Kotlin - Returns and jumps  (0) 2024.01.11