Глиф Учебники

Урок 6. Функции, параметры и значения по умолчанию

Курс «Kotlin» · урок 6 из 10 · 4 минуты чтения

Функция — именованный кусок программы, который можно вызывать много раз. Функции избавляют от повторения одинакового кода и делят большую программу на понятные части. main и println — тоже функции: первую пишете вы, вторая уже есть в Kotlin. В этом уроке — как писать свои.

Первая функция

Kotlin
fun square(x: Int): Int {
    return x * x
}

fun main() {
    println(square(5))
    println(square(12))
}
Вывод
25
144

Из чего состоит объявление:

При вызове square(5) значение 5 — аргумент — попадает в параметр x, функция считает 5 * 5 и возвращает

  1. Функции можно объявлять до main или после — порядок в файле не важен.
вызов println(square(5)) println( 25 ) вывод: 25 функция fun square(x: Int): Int { параметр тип результата x 5 return x * x 5 * 5 = 25 } аргумент 5 результат 25
Вызов square(5): аргумент 5 попадает в параметр x типа Int, функция считает x * x и возвращает 25 туда, где её вызвали, — в println.

Функция в одну строку

Если всё тело функции — одно выражение, фигурные скобки и return можно убрать и написать =:

Kotlin
fun square(x: Int) = x * x

fun larger(a: Int, b: Int) = if (a > b) a else b

fun main() {
    println(square(7))
    println(larger(3, 9))
}
Вывод
49
9

Тип результата здесь компилятор выводит сам. Параметров может быть сколько угодно — через запятую.

Функции без результата

Функция может ничего не возвращать, а только что-то делать — например, выводить:

Kotlin
fun greetAll(names: List<String>) {
    for (name in names) {
        println("Hello, $name!")
    }
}

fun main() {
    greetAll(listOf("Anna", "Oleg"))
}
Вывод
Hello, Anna!
Hello, Oleg!

Тип результата у такой функции — Unit («ничего полезного»), его не пишут. В Java и C на этом месте стоит void. return без значения в такой функции досрочно её заканчивает.

Параметры по умолчанию и именованные аргументы

Параметру можно дать значение по умолчанию — тогда при вызове его можно не указывать:

Kotlin
fun greet(name: String = "guest", times: Int = 1) {
    repeat(times) {
        println("Hello, $name!")
    }
}

fun main() {
    greet()
    greet("Anna")
    greet("Oleg", 2)
    greet(times = 2)
}
Вывод
Hello, guest!
Hello, Anna!
Hello, Oleg!
Hello, Oleg!
Hello, guest!
Hello, guest!
fun greet(name: String = "guest", times: Int = 1) вызов name times вывод greet() "guest" 1 Hello, guest! × 1 greet("Anna") "Anna" 1 Hello, Anna! × 1 greet("Oleg", 2) "Oleg" 2 Hello, Oleg! × 2 greet(times = 2) "guest" 2 Hello, guest! × 2 задано при вызове взято по умолчанию × — сколько раз
Четыре вызова greet: что не передано при вызове, берётся по умолчанию — name = "guest" и times = 1, а times = 2 можно задать по имени.

Именованные аргументы делают вызов понятнее, когда параметров много: createUser(name = "Anna", age = 20, admin = false) читается лучше, чем createUser("Anna", 20, false).

Функция, которая отвечает «да» или «нет»

Функции с результатом Boolean удобно называть с is: isPrime, isEmpty. Внутри может быть несколько return — функция заканчивается на первом сработавшем:

Kotlin
fun isPrime(n: Int): Boolean {
    if (n < 2) return false
    var d = 2
    while (d * d <= n) {
        if (n % d == 0) return false
        d++
    }
    return true
}

fun main() {
    for (n in 1..20) {
        if (isPrime(n)) {
            println(n)
        }
    }
}
Вывод
2
3
5
7
11
13
17
19

Делители проверяются только до корня из n (d * d <= n): если у числа есть делитель больше корня, то есть и меньше.

Параметры менять нельзя

Параметры функции ведут себя как val: присвоить им новое значение нельзя, x = x + 1 внутри функции не соберётся. Нужна изменяемая копия — объявите переменную: var current = x.

Переменные, объявленные внутри функции, видны только в ней. В разных функциях могут быть переменные с одинаковыми именами — они не мешают друг другу.

Рекурсия

Функция может вызывать сама себя — это рекурсия. Факториал: 5! = 5 · 4 · 3 · 2 · 1:

Kotlin
fun factorial(n: Int): Long = if (n <= 1) 1 else n * factorial(n - 1)

fun main() {
    println(factorial(5))
    println(factorial(20))
}
Вывод
120
2432902008176640000

У рекурсии обязательно должен быть случай, где она останавливается (n <= 1). Без него функция вызывает себя бесконечно, и программа падает с ошибкой StackOverflowError. Результат здесь — Long: уже 13! не помещается в Int.

вызовы вниз результаты вверх factorial(5) = 5 * factorial(4) 5 * 24 = 120 factorial(4) = 4 * factorial(3) 4 * 6 = 24 factorial(3) = 3 * factorial(2) 3 * 2 = 6 factorial(2) = 2 * factorial(1) 2 * 1 = 2 factorial(1) = 1 n <= 1 — стоп 1
factorial(5) вызывает factorial(4) и так далее до factorial(1), где рекурсия останавливается; результаты возвращаются вверх: 1, 2, 6, 24, 120.

Переменное число аргументов

vararg позволяет передать сколько угодно значений одного типа — внутри функции они видны как массив:

Kotlin
fun sumAll(vararg numbers: Int): Int {
    var s = 0
    for (n in numbers) {
        s += n
    }
    return s
}

fun main() {
    println(sumAll(1, 2, 3))
    println(sumAll(10, 20))
    println(sumAll())
}
Вывод
6
30
0

Лямбды

Функцию можно записать и без имени, прямо как значение, — это лямбда. Она пишется в фигурных скобках: параметры, стрелка -> и тело:

Kotlin
fun main() {
    val double = { x: Int -> x * 2 }
    println(double(4))
    val numbers = listOf(1, 2, 3)
    println(numbers.map { it * 10 })
}
Вывод
8
[10, 20, 30]

Если у лямбды один параметр, его можно не называть — он доступен как it. Лямбды постоянно встречаются при работе со списками: map { it * 10 } — «каждый элемент умножить на 10». Подробнее — в уроке Коллекции.

Коротко

Проверьте себя

1. Что выведет программа?
Kotlin
fun add(a: Int, b: Int = 10) = a + b

fun main() {
    println(add(5))
}
2. Как вызвать fun greet(name: String = "guest", times: Int = 1), задав только times?
3. Что значит тип Unit у функции?
4. Можно ли внутри функции fun f(x: Int) написать x = 5?