Kotlin: Компаньон-объекты и статические данные

Компаньон-объекты и статические данные

В Java для создания статических переменных и функций используется модификатор static. В Kotlin используется схожий механизм — компаньон-объект. Переменные и функции, объявленные внутри компаньон-объекта, становятся статическими.

Рассмотрим пример: создадим класс Sam с переменной count, изначально равной 0. В блоке init будем увеличивать count на единицу при создании каждого объекта и выводить сообщение о количестве созданных объектов.

class Sam {
    var count = 0

    init {
        count++
        println("Создано объектов: $count")
    }
}

fun main() {
    val test1 = Sam()
    val test2 = Sam()
    val test3 = Sam()
    val test4 = Sam()
}

При запуске кода выводится четыре сообщения «Создано объектов: 1». Переменная count не является общей для всех объектов, а создается для каждого объекта отдельно.

Чтобы сделать count общей для всех объектов класса, необходимо объявить её внутри компаньон-объекта:

class Sam {
    companion object {
        var count = 0
    }

    init {
        count++
        println("Создано объектов: $count")
    }
}

fun main() {
    val test1 = Sam()
    val test2 = Sam()
    val test3 = Sam()
    val test4 = Sam()
}

Теперь при запуске выводится «Создано объектов: 1», «Создано объектов: 2», «Создано объектов: 3», «Создано объектов: 4». Переменная count теперь принадлежит классу, а не каждому объекту. К ней можно обращаться через имя класса: Sam.count.

Изолированные классы

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

Создадим изолированный класс Db с различными типами баз данных:

sealed class Db(val id: Int, val con: String)

data class MongoDb(id: Int, con: String): Db(id, con)
data class MySql(id: Int, con: String): Db(id, con)
data class PostgreSql(id: Int, con: String, val isTest: Boolean): Db(id, con)

object Hulk : Db(1, "HulkConnection")

Теперь можно создавать объекты, выбирая нужный тип базы данных:

val db1 = MongoDb(5, "mongo")
val db2 = PostgreSql(10, "postgres", true)

Объекты, созданные на основе классов данных, имеют дополнительные возможности, например, функцию copy() для создания точной копии объекта и оператор == для сравнения объектов:

val dbCopy = db1.copy()
println(db1 == dbCopy) // Выведет true

val dbCopyModified = db1.copy(con = "modified")
println(db1 == dbCopyModified) // Выведет false

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

fun MongoDb.printInfo() {
    println("MongoDb id: $id, connection: $con")
}

val db3 = MongoDb(2, "anotherMongo")
if (db3 is MongoDb) db3.printInfo()

Функции высшего порядка

Рассмотрим создание функции, которая принимает другую функцию в качестве параметра. Создадим функцию filterList, которая принимает список строк и функцию-фильтр, возвращающую true или false:

fun filterList(list: List<String>, filter: (String) -> Boolean) {
    list.forEach { element ->
        if (filter(element)) println(element)
    }
}

val filter = { element: String -> element.startsWith("J") }
filterList(listOf("Java", "Kotlin", "JavaScript"), filter)

Функцию-фильтр можно передавать как отдельный параметр или хранить в переменной:

val filter2 = { element: String -> element.startsWith("K") }
filterList(listOf("Java", "Kotlin", "JavaScript"), filter2)

Функция filterList демонстрирует работу с функциями высшего порядка в Kotlin.

В этом уроке мы рассмотрели компаньон-объекты для создания статических членов класса, изолированные классы для создания расширяемых перечислений и функции высшего порядка, позволяющие передавать функции в качестве параметров другим функциям. Эти инструменты повышают гибкость и выразительность кода на Kotlin.

Что будем искать? Например,программа