Функции в Kotlin

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

Обычные функции и функции-расширения определяются за пределами класса. Их отличие заключается в наличии имени класса в нотации через точку перед именем функции-расширения. Например, MyClass.itsFunc() {}. Подробнее о методах рассказано в уроке об объектно-ориентированном программировании.

Все функции по-умолчанию, то есть без модификатора видимости, являются public. Их можно использовать отовсюду. Если функция верхнего уровня (не находится внутри класса) объявлена с модификатором private, ее можно использовать только в данном файле.

В Kotlin функции определяются через ключевое слово fun. После имени функции в круглых скобках через запятую перечисляются параметры. Каждый параметр состоит из имени переменной и через двоеточие ее типа. Параметры являются val-переменными. Ключевое слово val опускается. После круглых скобок с параметрами ставится двоеточие и указывается тип возвращаемого функцией значения. Тело функции заключается в фигурные скобки.

fun main() {
    val a = 8
    val b = 13
 
    val d = avr(a, b)
 
    println(d) // 10.5
}
 
fun avr(n1: Int, n2: Int): Float {
    return (n1 + n2).toFloat() / 2
}

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

fun avr(n1: Int, n2: Int) =
    (n1 + n2).toFloat() / 2

В этом случае компилятор Котлина сам выведет из выражения тип возвращаемого значения. Другой пример:

fun main() {
    val a = 3
    val b = 5
    val c = abNumber(a, b)
    println(c)
}
 
fun abNumber(n1: Int, n2: Int) =
    if (n1 > n2) {
        n1 / n2
    }
    else {
        n2 / n1
    }

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

fun main() {
    val a = hello()
    println(a) // kotlin.Unit
}
 
fun hello() {
    println("hi") // hi
}

Выполнение:

hi
kotlin.Unit

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

fun main() {
    repeatStr("Hi", 3) // HiHiHi
    println()
    repeatStr("Hello") // HelloHello
}
 
fun repeatStr(s: String, n: Int = 2) {
    for(i in 1..n)
        print(s)
}

Параметры со значениями по умолчанию лучше указывать последними. Иначе, при вызове функции следует использовать так называемые именованные аргументы – обращение к параметрам функции по именам их переменных:

fun main() {
    repeatStr(3, "Hi") // HiHiHi
    println()
    repeatStr(s = "Hello") // HelloHello
}
 
fun repeatStr(n: Int = 2, s: String) {
    for(i in 1..n)
        print(s)
}

Вообще, именованные аргументы можно использовать в любых случаях. При этом совпадение с последовательностью параметров не обязательно:

fun main() {
    repeatStr(s = "Hi", n = 3) // HiHiHi
    println()
    repeatStr(n = 2, s = "Hello") // HelloHello
}
 
fun repeatStr(s: String, n: Int) {
    for(i in 1..n)
        print(s)
}

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

fun main() {
    println(avr(0, 1, 8)) // 3.0
    println(avr(-5, 1)) // -2.0
}
 
fun avr(vararg nums: Int): Float {
    val sum = nums.sum()
    val l = nums.size
    return sum.toFloat() / l
}

Параметр vararg – это массив. То есть аргументы, переданные в этот параметр, упаковываются в массив элементов указанного типа. С другой стороны, если имеется уже готовый массив, а параметр функции объявлен как vararg, то есть ожидает произвольное количество отдельных аргументов, мы можем распаковать массив через звездочку:

fun main() {
    val a: IntArray = intArrayOf(0, 1, 8)
    println(avr(*a)) // 3.0
}
 
fun avr(vararg nums: Int): Float {
    val sum = nums.sum()
    val l = nums.size
    return sum.toFloat() / l
}

Если у функции есть другие аргументы, vararg лучше указывать последним. В иных случаях придется использовать именованные аргументы.

PDF-версия курса с дополнительными уроками

Приложение для Android "Kotlin. Курс"