Kotlin

[Kotlin] 코틀린에서의 상속(Inheritance)과 파생 클래스의 초기화 순서(Derived class initialization order), 슈퍼 클래스 구현 호출

yujinius 2024. 7. 22. 17:13

💗 상속 Inheritance

  • 클래스는 서로 상속될 수 있으며 상속 계층을 형성할 수 있음
  • 모든 Kotlin 클래스는 공통 슈퍼클래스인 Any를 가짐
  • 이는 슈퍼타입이 선언되지 않은 클래스의 기본 슈퍼클래스
class Example // 암묵적으로 Any를 상속
  • Any에는 세 가지 메서드가 있다:
    • equals(), hashCode(), toString().
    • 따라서 이 메서드들은 모든 Kotlin 클래스에서 사용할 수 있다.
  • 기본적으로 Kotlin 클래스는 final로 설정되어 있어 상속될 수 없다.
  • 클래스를 상속 가능하게 만들려면 open 키워드를 사용해야 한다.
  • open class Base // Class is open for inheritance
  • 명시적인 슈퍼타입을 선언하려면, 클래스 헤더에서 타입 뒤에 콜론을 사용
  • open class Base(p: Int) class Derived(p: Int) : Base(p)
  • 파생 클래스에 기본 생성자가 있는 경우,
    • 기본 생성자의 매개변수에 따라 기본 클래스를 초기화해야 한다.
  • 파생 클래스에 기본 생성자가 없는 경우,
    • 각 보조 생성자는 super 키워드를 사용하여 기본 타입을 초기화하거나 다른 보조 생성자에게 위임해야 한다. 이 경우, 다른 보조 생성자들이 기본 타입의 다른 생성자를 호출할 수 있다.
class MyView : View {
    constructor(ctx: Context) : super(ctx)
    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}

 

 코틀린 메소드 오버라이딩(overriding methods), 속성 오버라이딩(overriding properties) 참고 ▼

https://yujinius45.tistory.com/99

 

[Kotlin] 코틀린 메소드 오버라이딩(overriding methods), 속성 오버라이딩(overriding properties)

💗 메소드 오버라이딩 Overriding methodsKotlin에서는 오버라이딩 가능한 멤버와 오버라이드를 명시적으로 표시해야 한다.open class Shape { open fun draw() { /*...*/ } fun fill() { /*...*/ }}class Circle : Shape() { overr

yujinius45.tistory.com

 

💗 파생 클래스 초기화 순서

Derived class initialization order

  • 파생 클래스의 새 인스턴스를 생성하는 동안, 기본 클래스 초기화는 첫 번째 단계로 수행된다. 이는 파생 클래스의 초기화 로직이 실행되기 전에 발생한다.
open class Base(val name: String) {

    init { println("Initializing a base class") }

    open val size: Int = 
        name.length.also { println("Initializing size in the base class: $it") }
}

class Derived(
    name: String,
    val lastName: String,
) : Base(name.replaceFirstChar { it.uppercase() }.also { println("Argument for the base class: $it") }) {

    init { println("Initializing a derived class") }

    override val size: Int =
        (super.size + lastName.length).also { println("Initializing size in the derived class: $it") }
}
Constructing the derived class("hello", "world")
Argument for the base class: Hello
Initializing a base class
Initializing size in the base class: 5
Initializing a derived class
Initializing size in the derived class: 10

💫 기본 클래스 설계시 생성자, 속성, 초기화, init 블록에서 open 멤버를 사용하는 것을 피해야 하는 이유

  • 이로 인해 기본 클래스 생성자가 실행될 때, 파생 클래스에서 선언되거나 오버라이드된 속성들은 아직 초기화되지 않는다.
  • 기본 클래스 초기화 로직에서 이러한 속성을 직접 또는 간접적으로 사용하면 잘못된 동작이나 런타임 오류가 발생할 수 있다.
  • 따라서 기본 클래스를 설계할 때 생성자, 속성 초기화자, 또는 초기화 블록에서 open 멤버를 사용하는 것을 피해야 한다.

💗 슈퍼클래스 구현 호출 Calling the superclass implementation

  • 파생 클래스의 코드에서 super 키워드를 사용하여 슈퍼클래스의 함수와 속성 접근자 구현을 호출할 수 있다.
open class Rectangle {
    open fun draw() { println("Drawing a rectangle") }
    val borderColor: String get() = "black"
}

class FilledRectangle : Rectangle() {
    override fun draw() {
        super.draw()
        println("Filling the rectangle")
    }

    val fillColor: String get() = super.borderColor
}

  • 내부 클래스에서는 super 키워드를 외부 클래스 이름으로 한정하여 외부 클래스의 슈퍼클래스에 접근할 수 있다. super@Outer:
class FilledRectangle: Rectangle() {
    override fun draw() {
        val filler = Filler()
        filler.drawAndFill()
    }

    inner class Filler {
        fun fill() { println("Filling") }
        fun drawAndFill() {
            super@FilledRectangle.draw() // Calls Rectangle's implementation of draw()
            fill()
            println("Drawn a filled rectangle with color ${super@FilledRectangle.borderColor}") // Uses Rectangle's implementation of borderColor's get()
        }
    }
}
Drawing a rectangle
Filling
Drawn a filled rectangle with color black

 

 

💗 오버라이딩 규칙 Overriding rules

  • Kotlin에서 구현 상속은 다음 규칙에 의해 규제된다.:
    • 클래스가 즉시 슈퍼클래스로부터 동일한 멤버의 여러 구현을 상속받는 경우, 해당 멤버를 오버라이드하고 자체 구현을 제공해야 한다.
    • 상속된 구현이 어떤 슈퍼타입에서 가져온 것인지 나타내려면, 각괄호 안에 슈퍼타입 이름을 지정한 super를 사용한다: super<Base>:
    open class Rectangle {
        open fun draw() { /* ... */ }
    }
    
    interface Polygon {
        fun draw() { /* ... */ } // interface members are 'open' by default
    }
    
    class Square() : Rectangle(), Polygon {
        // The compiler requires draw() to be overridden:
        override fun draw() {
            super<Rectangle>.draw() // call to Rectangle.draw()
            super<Polygon>.draw() // call to Polygon.draw()
        }
    }
    
    • Rectangle과 Polygon 모두 상속받는 것은 괜찮지만, 둘 다 draw()의 구현을 가지고 있으므로 Square에서 draw()를 오버라이드하고 이 모호성을 제거하기 위해 별도의 구현을 제공해야 한다.

 

Java와 Kotlin의 상속 차이가 궁금하다면? ▼

https://yujinius45.tistory.com/100

 

[Kotlin/Java] Java와 Kotlin의 상속 차이점과 다중 상속, 오버라이딩 규칙 비교

❓Java와 Kotlin의 상속 차이점?Java에서의 상속클래스 상속:단일 상속: 한 클래스는 단 하나의 클래스만 상속받을 수 있음구문: class Child extends Parentclass Parent { // 부모 클래스 내용}class Child extends Par

yujinius45.tistory.com

 


참고 : https://kotlinlang.org/docs/inheritance.html#overriding-rules

 

Inheritance | Kotlin

 

kotlinlang.org