JavaRush /Курсы /Kotlin SELF /Объявление и вызов функций

Объявление и вызов функций

Kotlin SELF
8 уровень , 1 лекция
Открыта

1. Объявление функций

Когда вы только начинаете программировать, кажется, самый простой вариант — это написать весь код в main, сверху вниз. И это действительно нормально на первых шагах: мозг занят синтаксисом, а не архитектурой.

Но довольно быстро появляются два симптома: вы копируете одинаковые куски кода (и потом чините баги в пяти местах), а main превращается в простыню, которую страшно трогать. Функции — это способ дать имя повторяющемуся действию или вычислению, чтобы код читался как сценарий: «сначала спросили ввод, потом посчитали, потом вывели».

Представьте, что программа — это рецепт. main — это «что делаем по шагам», а функции — это «как именно нарезать лук» и «как сварить макароны».

Минимальная функция: fun имя() { ... }

Сначала разберём самый простой вид функции: без параметров и без результата. В Kotlin функция создается с помощью ключевого слова fun, затем идёт имя и круглые скобки с параметрами (пока пустые), затем тело в фигурных скобках.

Важно: функции (как и main) обычно объявляют на уровне файла, то есть не «внутри main». Локальные функции мы разберём позже, а сейчас будем придерживаться классики: функции — отдельно, main — отдельно.

fun printWelcome() {
    println("Добро пожаловать в программу!") // Добро пожаловать в программу!
}

fun main() {
    printWelcome()
    println("Сейчас начнём работать...")      // Сейчас начнём работать...
}

Здесь printWelcome() — это просто «команда». Мы вызвали её, и она выполнилась. Обратите внимание на две вещи.

Во-первых, объявление функции само по себе ничего не запускает. Пока вы не вызвали printWelcome() из main, программа не обязана даже вспоминать, что такая функция существует.

Во-вторых, имя функции — это не место для фантазии. Нормальные имена начинаются с действия: printWelcome, readAge, calculateTotal. С ними код начинает читаться почти как текст.

Параметры и аргументы

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

Параметры — это переменные, которые функция получает на вход. Они объявляются в круглых скобках и обязаны иметь тип. Аргументы — это конкретные значения, которые вы передаёте при вызове.

Давайте я оформлю вам эти факты в небольшую таблицу:

Термин Где живёт Пример Как понимать
Параметр в объявлении функции
name: String
«что функция ожидает на вход»
Аргумент в вызове функции
"Kotlin"
«что реально передали»

Теперь пример. Сделаем функцию, которая печатает приветствие для конкретного пользователя:

fun greet(name: String) {
    println("Привет, $name!") // Привет, Kotlin!
}

fun main() {
    greet("Kotlin")
    greet("мир")
}

Здесь name: String — параметр, а "Kotlin" и "мир" — аргументы. Обратите внимание: параметр name доступен только внутри тела функции. Функция завершилась — параметр исчез.

Функции с несколькими параметрами

Когда параметров несколько, они перечисляются через запятую. И тут начинают появляться первые «человеческие» ошибки: перепутать порядок аргументов легче, чем кажется (особенно если тип одинаковый, например два Int).

Сделаем функцию, которая печатает «рамку» для заголовка. Это выглядит как мелочь, но такие мелочи отлично тренируют мозг: вы сразу видите пользу от «дать имя повторяющемуся куску кода».

fun printTitle(title: String, width: Int) {
    println("=".repeat(width))
    println(title)
    println("=".repeat(width))
}

fun main() {
    printTitle("Отчёт", 10)
}

Если бы вы случайно вызвали printTitle(10, "Отчёт"), компилятор не дал бы скомпилировать программу, потому что типы не совпадают (и это хорошо). А вот если бы было printTitle(width: Int, height: Int) — компилятор бы пропустил, а логика бы сломалась. Поэтому говорящие имена параметров — это не только эстетика, но и защита от ошибок.

2. Возвращаемые значения и сигнатуры

Возвращаемый тип: функция как «выражение», а не просто «действие»

До этого мы делали функции, которые что-то печатают. Но очень часто функция должна посчитать и отдать результат обратно. Тогда говорят, что функция «возвращает значение».

В Kotlin возвращаемый тип пишется после списка параметров через двоеточие: : Int, : String, : Boolean и так далее. Пример из базового синтаксиса Kotlin показывает функцию sum(a: Int, b: Int): Int, которая возвращает сумму.

fun sum(a: Int, b: Int): Int {
    return a + b
}

fun main() {
    val total = sum(10, 20)
    println(total) // 30
}

Старайтесь не пугаться слова return: в следующей лекции мы разберём, как именно функция «завершается» и что значит «вернуть значение». Пока зафиксируем главное: если у функции указан возвращаемый тип : Int, то результат вызова можно присвоить переменной и использовать дальше.

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

fun sum(a: Int, b: Int): Int {
    return a + b
}

fun main() {
    println("2 + 3 = ${sum(2, 3)}") // 2 + 3 = 5
}

Мы буквально вставили результат функции в string template. Вы это уже умеете делать с переменными; теперь учимся делать с функциями.

Сигнатура функции как контракт

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

Возьмём пример:

fun average(total: Int, days: Int): Double {
    return total.toDouble() / days
}

Её сигнатура — это:

  • имя: average
  • параметры: (total: Int, days: Int)
  • возвращаемый тип: Double

И это уже почти всё, что вам нужно, чтобы пользоваться функцией. Даже не читая тело, можно понять: «дайте мне total и days, я верну Double».

Можно представить это небольшой схемой:

flowchart LR
    A["Аргументы в вызове<br/>average(7000, 7)"] --> B["Параметры функции<br/>total: Int, days: Int"]
    B --> C["Тело функции<br/>вычисления"]
    C --> D["Результат вызова<br/>Double"]

Если вы умеете читать сигнатуры, вы начинаете программировать быстрее: меньше прыгаете глазами по файлам, меньше полагаетесь на интуицию, больше — на контракт.

Параметры внутри функции — только для чтения

У новичков часто возникает желание сделать так: «я получил параметр, сейчас его подправлю и верну». Но Kotlin не даёт переназначать параметры. Это сделано намеренно: параметры — входные данные, а не рабочие переменные. Воспринимайте их как val-переменные. Ведь именно так их воспринимает компилятор Kotlin.

То есть вот так нельзя (компилятор ругнётся):

fun makeLouder(text: String): String {
    text = text.uppercase() // Ошибка компиляции: Val cannot be reassigned
    return text.uppercase()
}

Если вам нужно что-то менять, создайте локальную переменную:

fun normalizeName(name: String): String {
    var result = name.trim()
    result = result.replaceFirstChar { it.uppercase() }
    return result
}

fun main() {
    println(normalizeName("  kotlin  ")) // Kotlin
}

Тут name — входные данные, а result — рабочая зона.

3. Мини-приложение «Трекер шагов»

Сейчас мы соберём небольшой, но цельный пример, который поможет вам собрать всё изученное вместе. Пусть это будет консольный «трекер шагов за неделю»: программа спрашивает шаги за каждый день, сохраняет в массив и печатает итог.

Раньше вы бы написали всё в main, а теперь начнём выносить повторяющиеся шаги в функции. Мы сознательно делаем код простым, чтобы фокус был на функциях, а не на хитрых приёмах.

Функция-помощник для печати заголовка

Когда программа растёт, приятно иметь единый стиль вывода.

fun printSection(title: String) {
    println()
    println("=== $title ===")
}

fun main() {
    printSection("Трекер шагов")
}

Функция, которая читает число

Валидацию ввода мы будем делать позже, но уже сейчас можно вынести «попросить и прочитать» в отдельную функцию.

fun readInt(prompt: String): Int {
    print(prompt)
    val raw = readln().trim()
    val number = raw.toIntOrNull()

    if (number == null) return 0
    return number
}

fun main() {
    val x = readInt("Введите число: ")
    println("Вы ввели: $x") // например: Вы ввели: 12
}

Как работает return мы разберём чуть позднее, сейчас же воспринимайте это как «функция обязана отдать Int, поэтому в конце отдаёт число».

Функция чтения шагов за день

Теперь оборачиваем «ввод шагов за конкретный день» в функцию с параметром dayName.

fun readStepsForDay(dayName: String): Int {
    return readInt("Шаги за $dayName: ")
}

fun main() {
    val mondaySteps = readStepsForDay("Понедельник")
    println("Понедельник: $mondaySteps") // например: Понедельник: 5400
}

Заметьте, как код начинает читаться по-человечески: readStepsForDay("Понедельник") — почти предложение.

Функция подсчёта суммы по массиву

У нас есть массив IntArray. Мы уже умеем проходить его циклом и накапливать сумму. Теперь дадим этому действию имя.

fun sumSteps(steps: IntArray): Int {
    var total = 0
    for (x in steps) {
        total += x
    }
    return total
}

fun main() {
    val data = intArrayOf(3000, 5000, 4500)
    println(sumSteps(data)) // 12500
}

Эта функция полезна не только в трекере шагов: она также пример того, как функция упаковывает длинный цикл и возвращает итог.

Собираем всё в один сценарий main

Соберём минимальный рабочий «трекер недели» из маленьких функций. Этот кусок будет длиннее чем обычно — это нормально, потому что main по задумке и должен быть сценарием, который читает человек.

fun printSection(title: String) {
    println()
    println("=== $title ===")
}

fun readInt(prompt: String): Int {
    print(prompt)
    val raw = readln().trim()
    val number = raw.toIntOrNull()
    if (number == null) return 0
    return number
}

fun readStepsForDay(dayName: String): Int {
    return readInt("Шаги за $dayName: ")
}

fun sumSteps(steps: IntArray): Int {
    var total = 0
    for (x in steps) total += x
    return total
}

fun main() {
    printSection("Трекер шагов за неделю")

    val days = arrayOf("Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс")
    val steps = IntArray(7)

    for (i in days.indices) {
        steps[i] = readStepsForDay(days[i])
    }

    val total = sumSteps(steps)
    println("Итого шагов: $total") // например: Итого шагов: 42000
}

Что здесь важно и интересно:

  • У каждой функции есть понятное имя и небольшой объём.
  • Параметры показывают «что нужно для работы»: dayName, prompt, steps.
  • Возвращаемый тип показывает «что функция отдаёт»: Int.
  • main перестал быть свалкой деталей и превратился в сценарий: «покажи заголовок → собери данные → посчитай → выведи».

4. Типичные ошибки при объявлении и вызове функций

Ошибка №1: забыли указать тип параметра.
Новичков часто тянет написать fun greet(name) { ... }, как в некоторых языках. Kotlin так не умеет: тип параметра обязателен. Правильно fun greet(name: String) { ... }. Это сначала раздражает, а потом спасает от очень странных багов, когда «не то передали и не туда».

Ошибка №2: перепутали параметры и аргументы (и поэтому путаетесь в объяснениях).
Это кажется терминологией ради терминологии, но на практике помогает. Параметры — это то, что объявлено в функции, аргументы — то, что вы реально передали в вызове. Если вы называете всё «параметрами», в какой-то момент вы сами себе не сможете объяснить, что именно не совпало.

Ошибка №3: пытаетесь изменить параметр внутри функции.
После опыта с var хочется «подкрутить входное значение» прямо в параметре. Kotlin не разрешает переназначать параметры: они ведут себя как val. Если нужно менять — создайте локальную var и меняйте её. Код станет чуть длиннее, но в разы понятнее.

Ошибка №4: функция делает слишком много всего сразу.
Когда вы только начинаете, кажется удобным написать fun doAll() — «и ввод, и подсчёт, и печать». Но тогда функцию невозможно переиспользовать: вы хотели «только посчитать», а она ещё и печатает; хотели «только спросить», а она уже лезет в массив. Хорошее правило на старте: одна функция — один понятный шаг сценария или одно вычисление.

Ошибка №5: имена уровня f1, calc, doIt, потому что “и так понятно”.
Пока программа 20 строк — да, «и так понятно». Когда станет 200 — уже нет. Имя функции должно отвечать на вопрос «что она делает?» или «что она возвращает?». sumSteps читается без комментариев, а calc2 — как загадка из квеста.

1
Задача
Kotlin SELF, 8 уровень, 1 лекция
Недоступна
Консольные границы
Консольные границы
1
Задача
Kotlin SELF, 8 уровень, 1 лекция
Недоступна
Приветственный экран
Приветственный экран
1
Задача
Kotlin SELF, 8 уровень, 1 лекция
Недоступна
План комнаты
План комнаты
1
Задача
Kotlin SELF, 8 уровень, 1 лекция
Недоступна
Календарный фильтр
Календарный фильтр
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ