Урок 9. Классы, data class и object
Встроенных типов — Int, String, List — хватает для простых задач. Но программа про школу работает с учениками, программа магазина — с товарами. Класс позволяет описать свой тип: какие у него данные (свойства) и что он умеет (методы). Конкретное значение такого типа называется объектом.
Первый класс
class Person(val name: String, var age: Int) {
fun info(): String {
return "$name, $age"
}
fun birthday() {
age++
}
}
fun main() {
val p = Person("Anna", 20)
println(p.info())
p.birthday()
println(p.age)
println(p.name)
}Anna, 20
21
AnnaЧто здесь есть:
class Person(...)— объявление класса. В скобках — основной конструктор: какие значения нужны, чтобы создать объект;val name: Stringиvar age: Intв конструкторе — сразу и параметры, и свойства.val— свойство только для чтения,var— изменяемое;fun info()иfun birthday()внутри класса — методы, функции, которые работают со свойствами объекта;Person("Anna", 20)— создание объекта. Словаnew, как в Java, в Kotlin нет;p.age,p.info()— свойство и метод объекта через точку.
Объектов одного класса можно создать сколько угодно, у каждого свои значения свойств.
Свойства в теле класса и init
Свойства можно объявлять и внутри класса. Код, который должен выполниться при создании объекта, пишут в блоке init:
class Rectangle(val width: Int, val height: Int) {
val area = width * height
init {
println("Rectangle $width x $height created")
}
val isSquare: Boolean
get() = width == height
}
fun main() {
val r = Rectangle(3, 4)
println(r.area)
println(r.isSquare)
}Rectangle 3 x 4 created
12
falseval area = width * height— свойство, которое считается один раз при создании объекта;val isSquareсget()— свойство, которое считается заново при каждом обращении. Снаружи оно выглядит как обычное свойство:r.isSquare, без скобок.
private: скрыть лишнее
Свойство или метод с private видны только внутри класса. Так класс защищает свои данные от неправильных изменений снаружи:
class Account(val owner: String) {
private var balance = 0
fun deposit(amount: Int) {
if (amount > 0) {
balance += amount
}
}
fun show() = "$owner: $balance"
}
fun main() {
val acc = Account("Oleg")
acc.deposit(100)
acc.deposit(-50)
println(acc.show())
}Oleg: 100Строка acc.balance = 1000000 в main не соберётся: balance закрыт. Изменить его можно только через deposit, а там отрицательная сумма отбрасывается.
Как объект выглядит при выводе
Если вывести объект обычного класса, println(p), получится служебная запись вроде Person@4e50df2e — имя класса и число, разное при каждом запуске. Чтобы объект выводился понятно, классу нужен метод toString — или можно сделать класс data class.
data class
data class — класс для хранения данных. Компилятор сам добавляет в него полезные методы:
data class Point(val x: Int, val y: Int)
fun main() {
val a = Point(1, 2)
val b = Point(1, 2)
println(a)
println(a == b)
val c = a.copy(y = 5)
println(c)
val (x, y) = c
println("$x $y")
}Point(x=1, y=2)
true
Point(x=1, y=5)
1 5toString— понятный вывод:Point(x=1, y=2);equals— сравнение по содержимому: два объекта с одинаковыми свойствами равны (==даётtrue). У обычного класса два отдельно созданных объекта не равны, даже если свойства одинаковые;copy— копия с изменением отдельных свойств:a.copy(y = 5);- деструктуризация — разложить свойства по переменным:
val (x, y) = c.
Тело в фигурных скобках data class не обязательно — хватает одной строки. Зачем нужен data class и какие у него ограничения — в справке data class — зачем нужен.
object: единственный объект
Иногда нужен ровно один объект на всю программу — настройки, счётчик, журнал. Для этого вместо class пишут object:
object Counter {
var count = 0
fun increment() {
count++
}
}
fun main() {
Counter.increment()
Counter.increment()
println(Counter.count)
}2Создавать object не нужно и нельзя: он существует сам, обращение — по его имени. Такой шаблон называется синглтоном (singleton — одиночка).
companion object
Внутри класса можно объявить companion object — его свойства и методы вызываются через имя класса, без объекта. Так делают, например, «фабрики» — функции, которые создают объекты:
class User(val name: String) {
companion object {
fun guest() = User("guest")
}
}
fun main() {
val u = User.guest()
println(u.name)
}guestВ Java для этого служит слово static. В Kotlin его нет: общее для всего класса кладут в companion object, а просто функции — вне классов, на верхний уровень файла.
Коротко
class Person(val name: String, var age: Int)— класс с конструктором и двумя свойствами.- Объект создаётся без
new:Person("Anna", 20); свойства и методы — через точку. init { }— код при создании объекта; свойство сget()считается при каждом обращении.private— видно только внутри класса.data classдаётtoString, сравнение по содержимому,copyи деструктуризацию.object— единственный объект;companion object— общее для класса, вместоstatic.