English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Kotlin 상속

이 문서에서는 상속을 배웁니다. 더 구체적으로, 상속이 무엇인지 그리고 Kotlin에서 상속을 어떻게 구현하는지에 대해 알아보겠습니다.

상속은面向对象编程의 핵심 기능 중 하나로, 사용자가 기본 클래스에서 새로운 클래스(유도 클래스)를 생성할 수 있게 합니다.

유도 클래스는 기본 클래스의 모든 기능을 상속받으며, 자신만의 기능을 추가할 수 있습니다.

Kotlin 상속을 자세히 설명하기 전에, 다음 두篇文章을 읽는 것을 추천합니다:

상속의 이유는 무엇인가요?

여기서는 응용 프로그램에서 세 가지 역할이 필요하다고 가정해 보겠습니다-그리고수학 교사(MathTeacher),축구 선수(Footballer)과상인(Businessman).

모든 역할이 인간이라면 그들은 걸어다니고 말할 수 있습니다. 그러나 그들에게는 특별한 기술도 있습니다. 수학 교사는수학을 가르치기(teachMath)그리고 축구 선수는축구를 치고(playFootball)으로 상인은사업 운영(runBusiness).

세 가지 역할을 할 수 있는 세 가지 클래스를 독립적으로 만들 수 있습니다. 각 클래스는 걸어다니고 말하고 특별한 기술을 수행할 수 있습니다.

각 클래스에서 각 역할에 대해 같은 걸어다니기와 말하는 코드를 복사해야 합니다.

새로운 기능을 추가하려면 - 먹기(eat)를 위해서는 각 역할에 대해 같은 코드를 구현해야 합니다. 이는 오류(복사 중)와 코드의 중복을 쉽게 유발할 수 있습니다.

기본 기능을 가진 Person 클래스가 있을 때, 예를 들어, 걸어다니기、먹기와 자기 잠시 잠을 자리고, 역할에 따라 이러한 기능에 특별한 기술을 추가하면 훨씬 쉬워집니다. 이는 상속을 통해 완료됩니다.

상속을 사용하면 각 클래스에 대해 같은 walk()、talk()와 eat() 코드를 구현할 필요가 없습니다. 단순히상속그렇게 하면 됩니다.

따라서 MathTeacher(유도 클래스)는 Person(기본 클래스)의 모든 기능을 상속받아 teachingMath()라는 새로운 기능을 추가할 수 있습니다. 마찬가지로 Footballer 클래스는 Person 클래스의 모든 기능을 상속받아 playFootball()라는 새로운 기능을 추가할 수 있습니다. 이와 같이 계속됩니다.

이렇게 하면 코드가 더 간결하고 이해하기 쉽고 확장 가능합니다.

기억해야 할 중요한 점은:상속을 처리할 때, 각 유도 클래스는 그것이 '기본' 클래스인지 여부를 만족해야 합니다. 위의 예제에서 MathTeacher는 Person(인간)이고, Footballer는 Person(인간)입니다. '상인(Businessman)이 기업(Business)인 것'이라고 생각할 수는 없습니다.

Kotlin 상속

코드에서 이전에 논의한 것을 시도해 보겠습니다:

open class Person(age: Int) {
    //식사, 말하기, 걸어다니는 코드
}
class MathTeacher(age: Int): Person(age) {
    //수학 교사의 다른 특징
}
class Footballer(age: Int): Person(age) {
    //축구선수의 다른 특징
}
class Businessman(age: Int): Person(age) {
    // 상인의 다른 특징
}

이곳에서 Person은 기본 클래스이며, MathTeacher, Footballer, Businessman 클래스는 Person 클래스에서 파생된 클래스입니다.

기본 클래스 Person 앞에 있는 open 키워드는 매우 중요합니다.

기본적으로, Kotlin의 클래스는 최종적인 클래스입니다. Java를 잘 아시다면, 최종 클래스는 파생 클래스를 만들 수 없다는 것을 알 수 있습니다. 클래스에 注解을 사용하여 컴파일러는 그 클래스에서 새로운 클래스를 파생할 수 있게 합니다.

예제: Kotlin 상속

open class Person(age: Int, name: String) {
    init {
        println("저의 이름은 $name.")
        println("저의 나이는 $age")
    }
}
class MathTeacher(age: Int, name: String): Person(age, name) {
    fun teachMaths() {
        println("저는 초등학교에서 교사로 일하고 있습니다.")
    }
}
class Footballer(age: Int, name: String): Person(age, name) {
    fun playFootball() {
        println("저는 로스앤젤레스 갤럭시 팀에서 활동하고 있습니다.")
    }
}
fun main(args: Array<String>) {
    val t1 = MathTeacher(25, \
    t1.teachMaths()
    println()
    val f1 29, "Christiano")
    f1.playFootball()
}

이 프로그램을 실행할 때, 출력은 다음과 같습니다:

저의 이름은 Jack.
저의 나이는 25
저는 초등학교에서 교사로 일하고 있습니다.
저의 이름은 Cristiano.
저의 나이는 29
저는 로스앤젤레스 갤럭시 팀에서 활동하고 있습니다.

이곳에서 Person 클래스에서 두 개의 MathTeacher와 Footballer 클래스를 파생했습니다.

Person 클래스의 주요 생성자는 두 가지 속성을 선언했으며 age와 name이며, 초기화 블록을 가지고 있습니다. Person 파생 클래스(MathTeacher와 Footballer)의 객체는 기본 클래스의 초기화 블록(및 멤버 함수)에 접근할 수 있습니다.

MathTeacher와 Footballer派생 클래스는 각기자신의 멤버 함수 teachMaths()와 playFootball()를 가지고 있습니다. 이 함수들은 각각의 클래스 객체에서만 접근할 수 있습니다.

当创建 MathTeacher 类的对象 t1 时,

val t1 = MathTeacher(25, \

参数将传递给主构造函数。 在Kotlin中,创建对象时会调用 init 块。 由于 MathTeacher 是从Person类派生的,因此它将在基类(Person)中查找初始化程序块并执行它。 如果 MathTeacher 具有 init 块,则编译器还将执行派生类的init块。

接下来,使用t1.teachMaths()语句调用对象t1的teachMaths()函数。

创建类的对象 f1 时,该程序的工作原理类似。 它执行基类的init块。 然后,使用语句f1.playFootball()调用 Footballer 类的playFootball()方法。

重要说明:Kotlin继承

  • 如果该类具有主要构造函数,则必须使用主要构造函数的参数来初始化基类。在上面的程序中,两个派生类都有两个参数 age 和 name,并且这两个参数都在基类的主构造函数中初始化。
    这是另一个实例:

    open class Person(age: Int, name: String) {
        // some code
    }
    class Footballer(age: Int, name: String, club: String): Person(age, name) {
        init {
            println(\
        }
        fun playFootball() {
            println(\
        }
    }
    fun main(args: Array<String>) {
        val f1 29, \
    }

      在此,派生类的主要构造函数具有3个参数,而基类具有2个参数。请注意,基类的两个参数均已初始化。

  • 如果没有主构造函数,则每个基类都必须初始化基类(使用super关键字),或者委托给另一个执行此操作的构造函数。 例如

    fun main(args: Array<String>) {
        val p1 = AuthLog("Bad Password")
    }
    open class Log {
        var data: String = ""
        var numberOfData = 0
        constructor(_data: String) {
        }
        constructor(_data: String, _numberOfData: Int) {
            data = _data
            numberOfData = _numberOfData
            println("$data: $numberOfData 횟수")
        }
    }
    class AuthLog: Log {
        constructor(_data: String): this("From AuthLog -> + $_data", 10) {
        }
        constructor(_data: String, _numberOfData: Int): super(_data, _numberOfData) {
        }
    }

      이 프로그램이 어떻게 작동하는지 더 알고 싶다면, 다음을 방문하세요:Kotlin 다중 생성자.

멤버 함수와 속성 재정의

기본 클래스와 자식 클래스가 동일한 이름을 가진 멤버 함수(속성)를 포함하고 있는 경우, override 키워드를 사용하여 자식 클래스의 멤버 함수를 덮어쓰고 기본 클래스의 멤버 함수에 대해 open 키워드를 사용해야 할 수 있습니다.

예제: 멤버 함수 재정의

// 비어 있는 주 생성자
open class Person() {
    open fun displayAge(age: Int) {
        println("내 나이는 $age.")
    }
}
class Girl: Person() {
    override fun displayAge(age: Int) {}}
        println("내 가상 연령은 ${age - 5}.")
    }
}
fun main(args: Array<String>) {
    val girl = Girl()
    girl.displayAge(31)
}

이 프로그램을 실행할 때, 출력은 다음과 같습니다:

의 가상 나이는 26.

여기서 girl.displayAge(31) 자식 클래스 Girl의 displayAge() 메서드를 호출합니다.

기본 클래스의 속성을 비슷한 방식으로 덮어쓸 수 있습니다.

다음 예제를 학습하기 전에, 다음을 방문할 수 있습니다: Kotlin의 getter와 setter 작동 방식을 확인하세요.

//비어 있는 주 생성자
open class Person() {
    open var age: Int = 0
        get() = field
        set(value) {
            field = value
        }
}
class Girl: Person() {
    override var age: Int = 0
        get() = field
        set(value) {
            field = value - 5
        }
}
fun main(args: Array<String>) {
    val girl = Girl()
    girl.age = 31
    println("내 가상 나이는 $girl.age.")
}

이 프로그램을 실행할 때, 출력은 다음과 같습니다:

내 가상 연령은 26.

처음 보면, 우리는 age 속성에 대해 자식 클래스와 기본 클래스에서 override 및 open 키워드를 사용했습니다.

기본 클래스 멤버 호출

super 키워드를 사용하여 기본 클래스의 함수(속성 접근 포함)를 자식 클래스에서 호출할 수 있습니다. 이렇게 합니다:

open class Person() {
    open fun displayAge(age: Int) {
        println("내 실제 나이는 $age.")
    }
}
class Girl: Person() {
    override fun displayAge(age: Int) {}}
        //기본 클래스의 함수 호출
        super.displayAge(age)
        
        println("내 가상 연령은 ${age - 5}.")
    }
}
fun main(args: Array<String>) {
    val girl = Girl()
    girl.displayAge(31)
}

이 프로그램을 실행할 때, 출력은 다음과 같습니다:

내 실제 연령은 31.
내 가상 연령은 26.