Базовый синтаксис классов
#
Васильев Андрей Михайлович, 2024
Версии презентации
Классы
#
Класс — это элемент исходного кода приложения, описывающий абстрактный тип
данных и его частичную или полную реализацию
Классы в Kotlin объявляются с помощью использования ключевого слова class.
Объявление класса состоит из
- имени класса
- заголовка (указания типов его параметров, основного конструктора и т.п)
- и тела класса, заключённого в фигурные скобки
И заголовок, и тело класса являются необязательными составляющими. Если
у класса нет тела, фигурные скобки могут быть опущены
Конструкторы
#
Класс в Kotlin может иметь основной конструктор (primary constructor) и один или
более дополнительных конструкторов (secondary constructors)
Основной конструктор является частью заголовка класса, его объявление идёт сразу
после имени класса (и необязательных параметров).
class Person constructor(firstName: String) { /*...*/ }
Если у основного конструктора нет аннотаций и модификаторов видимости, ключевое
слово constructor может быть опущено.
class Person(firstName: String) { /*...*/ }
Инициализация
#
Основной конструктор не может содержать в себе исполняемого кода.
Инициализирующий код может быть помещён в соответствующие блоки (initializers
blocks), которые помечаются словом init.
При создании экземпляра класса блоки инициализации выполняются в том порядке, в
котором они идут в теле класса, чередуясь с инициализацией свойств.
class InitOrderDemo(name: String) {
val firstProperty = "Первое свойство: $name"
init {
println("Первый блок инициализации: ${name}")
}
val secondProperty = "Второе свойство: ${name.length}"
init {
println("Второй блок инициализации: ${name.length}")
}
}
Иницализация свойств
#
Обратите внимание, что параметры основного конструктора могут быть использованы в инициализирующем блоке. Они также могут быть использованы при инициализации свойств в теле класса.
class Customer(name: String) {
val customerKey = name.uppercase()
}
Компактная инициализация
#
Для объявления и инициализации свойств основного конструктора в Kotlin есть
лаконичное синтаксическое решение:
class Person(val firstName: String, val lastName: String, var age: Int)
Такие объявления также могут включать в себя значения свойств класса по
умолчанию.
class Person(val firstName: String, val lastName: String, var isEmployed: Boolean = true)
Вы можете использовать завершающую запятую при объявлении свойств класса.
class Person(
val firstName: String,
val lastName: String,
var age: Int, // завершающая запятая
) { /*...*/ }
Свойства, объявленные в основном конструкторе, могут быть изменяемые (var) и неизменяемые (val).
Наследование
#
Для всех классов в Kotlin родительским суперклассом является класс Any
. Он
также является родительским классом для любого класса, в котором не указан
какой-либо другой родительский класс.
class Example // Неявно наследуется от Any
У Any
есть три метода: equals()
, hashCode()
и toString()
. Эти методы
определены для всех классов в Kotlin
Разрешение наследования
#
По умолчанию все классы в Kotlin имеют статус final
, который блокирует
возможность наследования. Чтобы сделать класс наследуемым, его нужно пометить
ключевым словом open.
open class Base // Класс открыт для наследования
Для явного объявления суперкласса мы помещаем его имя за знаком двоеточия в
оглавлении класса:
open class Base(p: Int)
class Derived(p: Int) : Base(p)
Если у класса есть основной конструктор, базовый тип может (и должен) быть
проинициализирован там же, с использованием параметров основного конструктора.
Переопределение методов класса
#
Kotlin требует явно указывать модификаторы и для членов, которые могут быть
переопределены, и для самого переопределения.
open class Shape {
open fun draw() { /*...*/ }
fun fill() { /*...*/ }
}
class Circle() : Shape() {
override fun draw() { /*...*/ }
}
Для Circle.draw()
необходим модификатор override. В случае её отсутствия
компилятор выдаст ошибку. Если у функции типа Shape.fill()
нет модификатора
open, объявление метода с такой же сигнатурой в производном классе невозможно, с
override
или без. Модификатор open
не действует при добавлении к членам
final
класса
Член класса, помеченный override
, является сам по себе open
, т.е. он может
быть переопределён в производных классах. Если вы хотите запретить возможность
переопределения такого члена, используйте final
.
open class Rectangle() : Shape() {
final override fun draw() { /*...*/ }
}
Порядок инициализации производного класса
#
При создании нового экземпляра класса в первую очередь выполняется инициализация
базового класса (этому шагу предшествует только оценка аргументов, передаваемых
в конструктор базового класса) и, таким образом, происходит до запуска логики
инициализации производного класса.
open class Base(val name: String) {
init { println("Инициализация класса Base") }
open val size: Int =
name.length.also { println("Инициализация свойства size в класса Base: $it") }
}
class Derived(
name: String,
val lastName: String,
) : Base(name.replaceFirstChar { it.uppercase() }.also { println("Аргументы, переданные в конструктор класса Base: $it") }) {
init { println("Инициализация класса Derived") }
override val size: Int =
(super.size + lastName.length).also { println("Инициализация свойства size в классе Derived: $it") }
}
fun main() {
println("Построение класса Derived(\"hello\", \"world\")")
Derived("hello", "world")
}
Вспомогательные объекты
#
Объявление объекта внутри класса может быть отмечено ключевым словом companion.
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
Для вызова членов такого companion объекта используется имя класса.
val instance = MyClass.create()
Необязательно указывать имя вспомогательного объекта. Тогда он будет назван Companion.
class MyClass {
companion object { }
}
val x = MyClass.Companion
Члены класса могут получить доступ к private членам соответствующего вспомогательного объекта.