Классы

Классы в Kotlin объявляются с помощью использования ключевого слова class.

class Person { /*...*/ }

Объявление класса состоит из имени класса, заголовка (указания типов его параметров, основного конструктора и т.п) и тела класса, заключённого в фигурные скобки. И заголовок, и тело класса являются необязательными составляющими. Если у класса нет тела, фигурные скобки могут быть опущены.

class Empty

Конструкторы

Класс в 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".also(::println)
    
    init {
        println("Первый блок инициализации: ${name}")
    }
    
    val secondProperty = "Второе свойство: ${name.length}".also(::println)
    
    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).

Если у конструктора есть аннотации или модификаторы видимости, ключевое слово constructor обязательно, и модификаторы используются перед ним.

class Customer public @Inject constructor(name: String) { /*...*/ }

Для более подробной информации см. "Модификаторы доступа".

Дополнительные конструкторы

В классах также могут быть объявлены дополнительные конструкторы (secondary constructors), перед которыми используется ключевое слово constructor.

class Person(val pets: MutableList<Pet> = mutableListOf())
class Pet {
    constructor(owner: Person) {
        owner.pets.add(this) // добавляет этого питомца в список домашних животных своего владельца
    }
}

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

class Person(val name: String) {
    val children: MutableList<Person> = mutableListOf()
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

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

Даже если у класса нет основного конструктора на него все равно происходит неявная ссылка и блоки инициализации выполняются также.

cclass Constructors {
    init {
        println("Блок инициализации")
    }
    constructor(i: Int) {
        println("Constructor $i")
    }
}

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

Если вы не желаете иметь класс с открытым public конструктором, вам необходимо объявить пустой конструктор с соответствующим модификатором видимости.

class DontCreateMe private constructor () { /*...*/ }

В JVM компилятор генерирует дополнительный конструктор без параметров в случае, если все параметры основного конструктора имеют значения по умолчанию. Это делает использование таких библиотек, как Jackson и JPA, более простым с Kotlin, так как они используют пустые конструкторы при создании экземпляров классов.

class Customer(val customerName: String = "")

Создание экземпляров классов

Для создания экземпляра класса конструктор вызывается так, как если бы он был обычной функцией.

val invoice = Invoice()

val customer = Customer("Joe Smith")

В Kotlin нет ключевого слова new.

Создание экземпляров вложенных, внутренних и анонимных внутренних классов описано в разделе Вложенные классы.

Члены класса

Классы могут содержать в себе:

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

Классы могут быть производными друг от друга и формировать иерархии наследования. Узнайте больше о наследовании в Котлине.

Абстрактные классы

Класс может быть объявлен как abstract со всеми или некоторыми его членами. Абстрактный член не имеет реализации в своём классе. Обратите внимание, что нам не надо аннотировать абстрактный класс или функцию словом open - это и так подразумевается.

abstract class Polygon {
    abstract fun draw()
}
class Rectangle : Polygon() {
    override fun draw() {
        // рисование прямоугольника
    }
}

Можно переопределить неабстрактный open член абстрактным.

open class Polygon {
    open fun draw() {
        // некоторый метод рисования полигонов по умолчанию
    }
}
abstract class WildShape : Polygon() {
    // Классы, которые наследуют WildShape, должны предоставлять свой собственный
    // метод рисования вместо использования по умолчанию для полигона
    abstract override fun draw()
}

Вспомогательные объекты

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

В частности, если вы объявляете вспомогательный объект в своём классе, у вас появляется возможность обращаться к членам класса, используя только название класса в качестве классификатора.