kotlin

kotlin OOB- inheretance

slow333 2023. 2. 7. 15:02

Kotlin의 모든 클래스는  Any를 상속 받음

class Example // Implicitly inherits from Any
 

Anyequals(), hashCode() 및 toString()는 Any에 정의되어 있어 모든 클래스에서 사용 가능

기본적으로 Kotlin 클래스는 final 이므로 상속할 수 없습니다. 클래스를 상속 가능하게 만들려면 다음 open키워드 로 클래스를 표시하십시오.

open class Base // Class is open for inheritance
 

명시적 상위 유형을 선언하려면 클래스 헤더에서 콜론 뒤에 유형을 배치하십시오.

open class Base(p: Int)

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

파생(derived) 클래스에 기본 생성자가 있는 경우 매개 변수에 따라 해당 기본 생성자에서 기본 클래스를 초기화할 수 있으며 초기화해야 합니다.

파생 클래스에 기본 생성자가 없는 경우 각 보조 생성자는 super 키워드를 사용하여 기본 형식을 초기화 하거나 다른 생성자에 위임해야 합니다. 이 경우 다른 보조 생성자가 기본 유형의 다른 생성자를 호출할 수 있습니다.

class MyView : View {
    constructor(ctx: Context) : super(ctx)

    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}

Overriding methods

Kotlin에서는 override 생성자를 반드시 붙여야함

open class Shape {
    open fun draw() { /*...*/ }
    fun fill() { /*...*/ }
}

class Circle() : Shape() {
    override fun draw() { /*...*/ }
}

Circle.draw() override에 필요합니다 . open와 같은 함수에 수정자(open)가 없으면 하위 클래스에서 동일한 서명을 사용하여 메서드를 선언하는 것이 허용되지 않습니다. open은 final class에서 안됨

재정의를 금지하려면 final 하십시오.

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

Overriding 속성

overriding 메커니즘은 메서드에서와 동일한 방식으로 속성에서 작동합니다. 파생 클래스에서 다시 선언되는 슈퍼클래스에서 선언된 속성은 앞에 override가 있어야 하며 호환 가능한 유형이어야 합니다. 

open class Shape {
    open val vertexCount: Int = 0
}

class Rectangle : Shape() {
    override val vertexCount = 4
}
 

val 속성을 var 속성으로 재정의할 수도 있지만 그 반대는 불가능합니다. 이는 val속성이 본질적으로 get메서드를 선언하고 이를 재정의 하면 파생 클래스에서 var추가로 메서드를 선언하기 때문에 허용됩니다.set override기본 생성자에서 속성 선언의 일부로 키워드를 사용할 수 있습니다 .

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
}

Derived class 초기화 순서

파생 클래스의 새 인스턴스를 생성하는 동안 기본 클래스 초기화가 첫 번째 단계로 수행됩니다(기본 클래스 생성자 선행). 즉, 파생 클래스의 초기화 전에 기본 클래스가 먼저 수행됨

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") }
}

이는 기본 클래스 생성자가 실행될 때 파생 클래스에서 선언되거나 재정의된 속성이 아직 초기화되지 않았음을 의미합니다. 기본 클래스 초기화 에서 이러한 속성을 사용하면(직접적으로 재정의된 다른 open멤버 구현을 통해 간접적으로) 잘못된 동작이나 런타임 오류가 발생할 수 있습니다. 따라서 기본 클래스를 디자인할 때 open생성자, 속성 이니셜라이저 또는 init블록에서 멤버를 사용하지 않아야 합니다.

슈퍼클래스 구현 호출

파생 클래스의 코드는 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()
        }
    }
}

Overriding 규칙

Kotlin에서 구현 상속은 다음 규칙에 의해 규제됩니다. 클래스가 직속 슈퍼클래스에서 동일한 멤버의 여러 구현을 상속하는 경우 이 멤버를 재정의하고 자체 구현을 제공해야 합니다(아마도 상속된 것 중 하나 사용).

상속된 구현이 취해진 상위 유형을 표시하려면 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 class에서 draw()을 재정의 하고 모호성을 제거하기 위해 별도의 구현을 제공해야 합니다.

 

sample code

fun main(){

    val audi = Car("bmw", "aos", 300.0)
    val tesla = ElectricCar("S-model", "Tesla", 22, LocalDate.now().year -1, 250.0)

    tesla.chargerType = "type 10"
    audi.drive(100.0)
    tesla.drive(200.0)
    println("베터리 수명 : ${tesla.batteryLife}")
    println("출고 년도 : ${tesla.year}")
    println( tesla.drive()+", tesla max speed :  "+tesla.maxSpeed)
    tesla.brake()
    audi.brake()
}

interface Drivable{
    val maxSpeed : Double
    fun drive() : String
    fun brake() {
        println("브레이크 밟아요!")
    }
}
// Sub Class, Child Class or Derived Class of Vehicle
// Super class, parent Class, Base Class of ElectricCar
open class Car(val name : String, val brand: String, override val maxSpeed: Double) : Drivable {

    open var range: Double = 0.0

    fun extendRange(amount: Double) {
        if(amount > 0)
            range += amount
    }
    fun drive(distance: Double) { // overload
        println("fun drove for $distance KM")
    }

    override fun drive(): String {
        return "운전 중입니다."
    }
}

// Sub Class, Child Class or Derived Class of Car
class ElectricCar(name: String, brand: String, var batteryLife: Int, var year: Int, maxSpeed: Double)
    : Car(name, brand, maxSpeed){

    var chargerType = "Type1"
    override var range: Double = batteryLife * 2.0

    override fun drive(): String {
        return "Drove for $range"
    }

    override fun brake() {
//        super.brake()
        println("전기차에서 밟는 브레이크")
    }
}