Наследование

Последнее обновление: 03.03.2024

Наследование позволяет создавать классы, которые расширяют функциональность или изменяют поведение уже существующих классов. В отношении наследования выделяются два ключевых компонента. Прежде всего это базовый класс (класс-родитель, родительский класс, суперкласс), который определяет базовую функциональность. И производный класс (класс-наследник, подкласс), который наследует функциональность базового класса и может расширять или модифицировать ее.

Чтобы функциональность класса можно было унаследовать, необходимо определить для этого класса аннотацию open. По умолчанию без этой аннотации класс не может быть унаследован.

open class базовый_класс
class производный_класс: первичный_конструктор_базового_класса

Для установки наследования после названия производного класса идет двоеточие. После двоеточия в общем случае вызывается первичный конструктор класса, от которого идет наследование. Рассмотрим пример:

open class Person{
    var name: String = "Undefined"
	fun printName() = println(name)
}
class Employee: Person()

Например, в данном случае класс Person представляет человека, который имеет свойство name (имя человека) и метод printName() для вывода информации о человеке. Класс Employee представляет условного работника. Поскольку работник является человеком, то класс работника будет разделять общий функционал с классом человека. Поэтому вместо того, чтобы заново определять в классе Employee свойство name, лучше уснаследовать весь функционал класса Person. То есть в данном случае класс Person является базовым или суперклассом, а класс Employee - производным классом или классом-наследником.

Но стоит учитывать, что при наследовании производный класс должен вызывать первичный конструктор (а если такого нет, то конструктор по умолчанию) базового класса.

Здесь класс Person явным образом не определяет первичных конструкторов, поэтому в классе Employee надо вызывать конструктор по умолчанию для класса Person

Вызвать конструктор базового класса в производном классе можно двумя способами. Первый способ - после двоеточия сразу указать вызов конструктора базового класса:

class Employee: Person()

Здесь запись Person() как раз представляет вызов конструктора по умолчанию класса Person.

Второй способ вызвать конструктор базового класса - определить в производном классе вторичный конструктор и в нем вызвать конструктор базового класса с помощью ключевого слова super:

open class Person{
    var name: String = "Undefined"
	fun printName() = println(name)
}
class Employee: Person{

    constructor() : super()
}

Здесь с помощью ключевого слова constructor в классе Employee определяется вторичный конструктор. А после списка его параметров после двоеточия идет обращение к конструктору базового класса: constructor() : super(). То есть здесь вызов super() - это и есть вызов конструктора базового класса.

Вне зависимости какой способ будет выбран, далее мы сможем создавать объекты класса Employee и использовать для него уснаследованный от класса Person функционал:

fun main() {

    val bob: Employee = Employee()
    bob.name = "Bob"
    bob.printName()     // Bob
}
open class Person{
    var name: String = "Undefined"
    fun printName() = println(name)
}
class Employee: Person()

Стоит отметить, что те классы, которые явным образом не наследуются от других классов, неявно наследуются от класса Any.

Наследование класса с первичным конструктором

Если базовый класс явным образом определяет конструктор (первичный или вторичный), то производный класс должен вызывать этот конструктор. Для вызова конструктора базового в производном применяются те ж способы.

Первый способ - вызвать конструктор после названия класса через двоеточие:

open class Person(val name: String){
    fun printName() = println(name)
}
class Employee(empName: String): Person(empName)

В данном случае класс Person через конструктор устанавливает свойство name. Поэтому в классе Employee тоже определен конструктор, который принимает стороковое значение и передает его в конструктор Person.

Если производный класс не имеет явного первичного конструктора, тогда при вызове вторичного конструктора должен вызываться конструктор базового класса через ключевое слово super:

open class Person(val name: String){
    fun printName()= println(name)
}
class Employee: Person{

    constructor(empName: String) : super(empName){}
}

Опять же, поскольку конструктор Person принимает один параметр, то в super() нам надо передать значение для этого параметра.

Применение классов:

fun main() {

    val bob = Employee("Bob")
    bob.printName()
}

open class Person(val name: String){
    fun printName() = println(name)
}
class Employee(empName: String): Person(empName)

Выше рассматривался случай, когда в базовом классе определен первичный конструктор.Но все то же действует и в том случае, если в базовом классе есть только вторичные конструкторы:

fun main() {

    val bob = Employee("Bob")
    bob.printName()
}

open class Person{

    val name: String
    constructor(userName: String){
        name = userName
    }
    fun printName() = println(name)
}
class Employee(empName: String): Person(empName)

Расширение базового класса

Производный класс наследует функционал от базового класса, но также может определять и свой собственный функционал:

fun main() {

    val bob = Employee("Bob", "JetBrains")
    bob.printName()
    bob.printCompany()
}

open class Person(val name: String){
    fun printName() = println(name)
}
class Employee(empName: String, val company: String): Person(empName){

    fun printCompany() = println(company)
}

В данном случае класс Employee добаваляет к унаследованному функционалу свойство company, которое хранит компанию работника, и функцию printCompany().

Стоит отметить, что в Kotlin мы можем унаследовать класс только от одного класса, множественное наследование не поддерживается.

Также, стоит отметить, что все классы по умолчанию наследуются от класса Any, даже если класс Any явным образом не указан в качестве базового. Поэтому любой класс уже по умолчанию будет иметь все свойства и функции, которые определены в классе Any. Поэтому все классы по умолчанию уже будут иметь такие функции как equals, toString, hashcode.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850