--- title: Функции description: Объявление функций, значения по умолчанию, именованные аргументы, vararg и локальные функции tags: - kotlin - syntax --- Функция объявляется словом `fun`. В Kotlin она может жить на верхнем уровне файла, внутри класса или внутри другой функции. ```kotlin fun greet(name: String): String { return "Привет, $name" } fun main() { println(greet("Kotlin")) // Привет, Kotlin } ``` Порядок: `fun` → имя → параметры → `:` тип результата → тело. ## Выражение вместо тела Если функция - одно выражение, фигурные скобки и `return` убираются: ```kotlin fun square(x: Int): Int = x * x fun greet(name: String) = "Привет, $name" // тип выведен: String fun max(a: Int, b: Int) = if (a > b) a else b ``` Это основная форма в идиоматичном Kotlin. Тип результата можно опустить - компилятор выведет его. Но для публичного API его лучше писать явно: это контракт, и его случайное изменение не должно быть незаметным. ## Unit - отсутствие результата ```kotlin fun log(message: String): Unit { println(message) } fun log2(message: String) { // : Unit можно опустить println(message) } ``` `Unit` - настоящий тип с единственным значением, а не пустота. Подробнее: [Any, Unit, Nothing](../Базовые%20типы%20ООП/Any,%20Unit,%20Nothing.md) ## Параметры Параметры всегда **только для чтения** - это `val`, присвоить им нельзя: ```kotlin fun bad(x: Int) { // x = 5 // ошибка компиляции val y = x + 1 println(y) } ``` Тип параметра обязателен - вывода типов здесь нет. ## Значения по умолчанию ```kotlin fun connect(host: String, port: Int = 8080, secure: Boolean = false) { println("$host:$port secure=$secure") } fun main() { connect("localhost") // localhost:8080 secure=false connect("localhost", 443) // localhost:443 secure=false connect("localhost", 443, true) // localhost:443 secure=true } ``` Это заменяет перегрузки: вместо трёх версий метода - одна функция. Значением по умолчанию может быть любое выражение, включая вызов функции и другие параметры: ```kotlin fun repeat2(text: String, times: Int = text.length) = text.repeat(times) fun main() { println(repeat2("ab")) // abab — times = 2 println(repeat2("ab", 3)) // ababab } ``` Выражение вычисляется при каждом вызове, в котором аргумент опущен. ## Именованные аргументы Аргумент можно передать по имени параметра: ```kotlin fun connect(host: String, port: Int = 80, secure: Boolean = false, timeout: Int = 30) { println("$host:$port s=$secure t=$timeout") } fun main() { connect("localhost", secure = true) // пропустили port connect(host = "localhost", timeout = 60) connect(port = 443, host = "localhost") // порядок не важен } ``` Две причины использовать: 1. **Пропустить** часть параметров со значениями по умолчанию 2. **Читаемость** на вызовах с непонятными литералами ```kotlin // что здесь true? // createUser("Иван", true, false, true) // так понятно: // createUser("Иван", admin = true, banned = false, verified = true) ``` Правило: позиционные аргументы идут до именованных. После первого именованного все следующие тоже должны быть именованными - кроме `vararg` со spread. ## vararg Переменное число аргументов: ```kotlin fun sum(vararg nums: Int): Int { var total = 0 for (n in nums) total += n // nums имеет тип IntArray return total } fun main() { println(sum(1, 2, 3)) // 6 println(sum()) // 0 val arr = intArrayOf(1, 2, 3) println(sum(*arr)) // 6 — spread-оператор обязателен } ``` Правила: - `vararg` может быть **только один** в функции - он не обязан быть последним, но тогда остальные передаются по имени - внутри функции это массив (`Array` или специализированный) - для передачи готового массива нужен `*` (spread) ```kotlin fun tag(vararg classes: String, id: String) = "$id: ${classes.joinToString()}" fun main() { println(tag("a", "b", id = "x")) // x: a, b } ``` ## Локальные функции Функция внутри функции - видит переменные внешней: ```kotlin fun process(items: List): List { val prefix = ">> " // захватывается fun format(x: Int): String = "$prefix$x" // локальная функция return items.map { format(it) } } fun main() { println(process(listOf(1, 2))) // [>> 1, >> 2] } ``` Нужны, когда логика используется только внутри одной функции и не стоит того, чтобы выносить её в класс или в приватную функцию файла. ## Перегрузка Функции с одним именем и разными параметрами: ```kotlin fun show(x: Int) = "число $x" fun show(x: String) = "строка $x" fun show(x: Int, y: Int) = "пара $x,$y" fun main() { println(show(1)) // число 1 println(show("a")) // строка a println(show(1, 2)) // пара 1,2 } ``` Сигнатуру образуют имя и типы параметров. Тип результата в неё **не входит** - две функции, отличающиеся только им, не скомпилируются. Чаще всего перегрузка не нужна: её заменяют значения по умолчанию. ## Передача аргументов Аргументы передаются **по значению**, как в Java: - примитив - копируется значение - объект - копируется ссылка Значит, переприсвоить параметр внутри функции нельзя (он `val`), но изменяемый объект изменить можно: ```kotlin fun addItem(list: MutableList) { list.add(99) // объект изменён — виден снаружи } fun main() { val items = mutableListOf(1) addItem(items) println(items) // [1, 99] } ``` ## Однострочные функции-расширения Функции можно добавлять к существующим типам: ```kotlin fun String.shout() = uppercase() + "!" fun main() { println("привет".shout()) // ПРИВЕТ! } ``` Подробнее: [Функции-расширения](../../Функциональный%20Kotlin/Функции-расширения.md) ## Функции как значения Функция может быть параметром и результатом: ```kotlin fun apply2(x: Int, f: (Int) -> Int) = f(x) fun main() { println(apply2(3) { it * 2 }) // 6 println(apply2(3, ::square)) // 9 — ссылка на функцию } fun square(x: Int) = x * x ``` Подробнее: [Лямбды и функциональные типы](Лямбды%20и%20функциональные%20типы.md), [Функции высшего порядка](../../Функциональный%20Kotlin/Функции%20высшего%20порядка.md) > [!info] Отличия от Java > | Java | Kotlin | > |---|---| > | метод только внутри класса | функция на верхнем уровне | > | `int f(int x) { return x; }` | `fun f(x: Int) = x` | > | `void` | `Unit` (можно опустить) | > | нет значений по умолчанию | `fun f(x: Int = 0)` | > | нет именованных аргументов | `f(x = 1)` | > | `int... nums` | `vararg nums: Int` | > | `f(arr)` для varargs | `f(*arr)` | > | параметр можно переприсвоить | параметр - `val` | > | перегрузки для опциональных параметров | одна функция со значениями по умолчанию | > > Значения по умолчанию + именованные аргументы убирают главный источник шума в Java API - «телескопические» перегрузки: > ```java > void connect(String host) { connect(host, 8080); } > void connect(String host, int port) { connect(host, port, false); } > void connect(String host, int port, boolean secure) { ... } > ``` > В Kotlin это одна строка. Для вызова из Java есть `@JvmOverloads`, который генерирует такие перегрузки автоматически - см. [Kotlin из Java](../../Взаимодействие%20с%20Java/Kotlin%20из%20Java.md). > > Сравнить: [Функции в Java](../../../Программирование%20на%20Java/Синтаксис%20и%20типы/Работа%20с%20данными%20и%20типами/Функции.md) > [!warning] Подводные камни > **Изменяемое значение по умолчанию вычисляется на каждом вызове** - в отличие от Python, где оно создаётся один раз. Это как раз правильное поведение: > ```kotlin > fun add(item: Int, list: MutableList = mutableListOf()): List { > list.add(item) > return list > } > > fun main() { > println(add(1)) // [1] > println(add(2)) // [2] — новый список, а не [1, 2] > } > ``` > > **Значения по умолчанию не видны из Java.** Без `@JvmOverloads` Java-код обязан передавать все аргументы. > > **Переопределённый метод не может изменить значения по умолчанию:** > ```kotlin > open class A { open fun f(x: Int = 1) = x } > class B : A() { > override fun f(x: Int) = x * 2 // значение по умолчанию наследуется, > } // переобъявить его нельзя > ``` > > **`vararg` без spread принимает массив как один элемент:** > ```kotlin > fun show(vararg items: Any) = items.size > > fun main() { > val arr = arrayOf(1, 2, 3) > println(show(arr)) // 1 — массив целиком как один аргумент > println(show(*arr)) // 3 — распакован > } > ``` > > **Перегрузка + значения по умолчанию = неоднозначность.** Компилятор выберет наиболее специфичную перегрузку, а не ту, что со значениями по умолчанию: > ```kotlin > fun f(x: Int, y: Int = 0) = "два" > fun f(x: Int) = "один" > > fun main() { > println(f(1)) // "один" — перегрузка без default побеждает > } > ``` > Такого сочетания лучше избегать. Дополнительно: [Лямбды и функциональные типы](Лямбды%20и%20функциональные%20типы.md) Далее: [Лямбды и функциональные типы](Лямбды%20и%20функциональные%20типы.md)