Функции в Kotlin для начинающих

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

Содержание статьи

В этом уроке вы узнаете, как создавать собственные функции, и увидите, как Kotlin упрощает их использование.

Создание новой функции в Kotlin

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

Приведенный выше код известен как объявление функции.

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

Подробнее о необходимости этих скобок обсудим ниже.

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

Вывод будет следующим:

В прошлых уроках мы уже использовали функции. Функция println выводит результат на консоль. Ниже вы узнаете, как передавать данные в функцию и получать данные взамен.

Параметры функции в Kotlin

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

В качестве примера рассмотрим следующую функцию:

Здесь дается определение одного параметра value типа Int в круглых скобках после названия функции. В любой функции круглые скобки содержат так называемый список параметров. Эти круглые скобки необходимы как при объявлении, так и при вызове функции, даже если список параметров пуст.

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

На заметку: Не путайте термины «параметр» и «аргумент». Функция объявляет свои параметры в списке параметров. При вызове функции вы предоставляете значения в качестве аргументов для параметров функции.

Можно написать функцию более обобщенной. С двумя параметрами, функция выведет результат умножение любых двух значений.

Теперь в скобках после названия функции есть два параметра — multiplier и andValue, оба типа Int.

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

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

Вы также можете назначить параметрам значения по умолчанию:

Мы добавили = 1 после второго параметра. Это значит, что при отсутствии значения для второго параметра значение по умолчанию будет равно 1.

Таким образом, вывод кода будет следующим:

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

Функции и оператор возврата return в Kotlin

Все рассматриваемые до сих пор функции выполняли простую задачу — они что-то выводили в консоль. Функции также могут возвращать значение. При вызове функции можно присвоить возвращаемое значение переменной или константе или использовать его непосредственно в if или when выражениях в качестве значения для проверки.

Это означает, что функцию можно использовать для управления данными. Данные просто принимаются через параметры, они изменяются внутри функции, а затем возвращаются.

Определить функцию, возвращающую значение, можно следующим образом:

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

Внутри функции используется оператор return для возврата значения. В этом примере возвращается результат от умножения двух параметров.

Также можно вернуть несколько значений с помощью Pair:

Данная функция возвращает результат от умножения и деления двух параметров в виде Pair, содержащую два Int значения.

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

В таком случае тип возвращаемого функцией значения выводится как тип выражения, присвоенного функции. В примере выше возвращаемым типом является Int, так как number и multiplier также принадлежат к типу Int.

Параметры в качестве значений в Kotlin

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

Рассмотрим следующим пример кода:

Результатом выполнения данного кода будет ошибка:

Значение из параметра value эквивалентно константе, объявленной с помощью val, и поэтому его нельзя переназначить. По этой причине, когда функция пытается увеличить его, компилятор выдает ошибку.

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

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

На заметку: Как вы увидите в будущем, при добавлении параметров к основному конструктору при определении класса вы действительно добавляете var или val к параметрам. Это делается для указания, что параметры являются свойствами класса и что их значение может или не может быть изменена.

Перегрузка функций в Kotlin

Что делать, если вам нужно использовать несколько функций с одним и тем же названием?

Это перегрузка, которая дает возможность определить похожие функции, используя одинаковое название для них, НО с разными типами параметров.

Однако, компилятор по-прежнему должен видеть разницу между данными функциями внутри текущей области видимости. Каждый раз при вызове функции должно быть понятно, какая функция будет выполняться.

Обычно это достигается через разницу в списке параметров:

  • Разное количество параметров;
  • Разные типы у параметров.

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

К примеру, такое определение двух методов приведет к ошибке:

У методов выше одинаковые названия, типы параметров и количество параметров. Kotlin не может различить их!

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

Задания для проверки

  1. Напишите функцию под названием printFullName, которая принимает две строки — firstName и lastName. Функция должна вывести полное имя и фамилию, определенное как firstName + " " + lastName. Используйте ее для вывода своего полного имени;
  2. Вызовите функцию printFullName, используя именованные аргументы;
  3. Напишите функцию под названием calculateFullName, которая возвращает полное имя в виде строки. Используйте ее для сохранения собственного имени в константе;
  4. Измените функцию calculateFullName, чтобы вернуть Pair, содержащую как полное имя, так и длину имени. Вы можете узнать длину строки, используя свойство length. Используйте эту функцию, чтобы определить длину собственного полного имени.

Функции как переменные в Kotlin

Функции в Kotlin являются просто еще одним типом данных. Их можно присваивать переменным и константам как и значения любого другого типа вроде Int или String.

Рассмотрим следующую функцию:

Данная функция принимает два параметра и возвращает сумму их значений.

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

Здесь названием переменной является function и ее тип выводится как (Int, Int) -> Int из присвоенной функции add. Переменная function состоит из типа функции, которая принимает два параметра Int и возвращает Int.

Теперь можно использовать переменную function так же, как использовалась бы функция add:

Данный код выведет на экран: 6

Теперь рассмотрим следующий код:

Здесь объявляется другая функция, которая принимает два параметра Int и возвращает Int. Вы можете назначить переменную function, используемую ранее, на новую функцию subtract, потому что список параметров и тип возвращаемого значения subtract совместимы с типом переменной function.

На этот раз вызов function возвращает 2.

Назначение функций переменным может быть очень удобным, потому что таким образом вы можете передавать функции другим функциям. Далее дан показательный этому пример:

Функция printResult принимает три параметра:

  1. function типа функции, которая принимает два параметра Int и возвращает Int, объявление выглядит следующих образом (Int, Int) -> Int;
  2. a с типом Int;
  3. b с типом Int.

Функция printResult вызывает переданную функцию из function, передавая в нее параметры a и b. Затем, полученный результат выводится в консоль:

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

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

Невозвращаемые функции в Kotlin

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

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

Эти циклы событий часто запускаются в приложении путем вызова функции, которая никогда не возвращается. Если вы начинаете разрабатывать приложения для Android, помните об этом, когда столкнетесь с основным потоком, также известным как поток UI, или поток пользовательского интерфейса.

У Kotlin есть способ сообщить компилятору, что функция никогда не вернет значение. Вы устанавливаете тип возвращаемого значения функции на тип Nothing, указывая на то, что функция никогда ничего не возвращает.

Грубая, но честная реализация невозвращаемой функции будет выглядеть следующим образом:

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

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

Создание хороших функций в Kotlin

Есть много способов решить проблемы при помощи функций. Лучшие (то есть самые простые в использовании и понимании) функции решают одну простую задачу и не пытаются сделать что-то еще. Это упрощает их сборку в более сложные модели поведения. У хороших функций есть четкий набор входных данных, которые каждый раз приводят к одному и тому же результату. Это облегчает процесс проверки правильности выполнения кода. Помните об этих практических правилах при создании функций.

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

Итоговые задачи для проверки

Задание 1: Нахождение простого числа

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

Сначала запишите следующую функцию:

Вы будете использовать данную функцию, чтобы определить, делится ли одно число на другое. Должно возвращаться true, когда divisor делится на number без остатка.

Подсказка: Можете использовать оператор нахождения остатка от деления (%).

Затем напишите главную функцию:

Функция вернет значение true если число из number является простым или false если это не так. Число является простым, только если оно делится на 1 и на само себя без остатка. Вы должны перебрать числа от 1 до рассматриваемого числа и найти делители.

Если у числа есть делители, отличные от 1 и самого себя, то число не является простым. Вам нужно будет использовать функцию isNumberDivisible(), которую вы написали ранее.

Используйте эту функцию для проверки следующих случаев:

Подсказка 1: Числа меньше 0 не рассматриваются в качестве простых. Сделайте проверку в начале функции и вернитесь раньше, если число меньше 0.

Подсказка 2: Используйте цикл for, чтобы найти делители. Если вы начинаете с 2 и заканчиваете перед самим числом, то сразу после нахождения делителя вы можете вернуть false;

Подсказка 3: Если вы хотите сделать что-то действительно умное, можете просто сделать цикл от 2 до получения квадратного корня из number, а не до самого числа. Можете сделать это как дополнительное упражнение. К примеру, рассмотрите число 16, квадратный корень которого равен 4. Делителями 16 будут 1, 2, 4, 8 и 16.

Задание 2: Рекурсивные функции

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

Напишите функцию, которая вычисляет значение из последовательности Фибоначчи. Любое значение в последовательности является суммой двух предыдущих значений. Последовательность определена таким образом, что первые два значения равны 1. То есть fibonacci(1) = 1 и fibonacci(2) = 1.

Напишите функцию, используя следующее объявление:

Затем проверьте, если вы написали функцию правильно, выполнив её со следующими числами:

Подсказка 1: Для значений number меньше 0 должен возвращаться 0;

Подсказка 2: Чтобы дать старт последовательности, код вернет значение 1, когда number равен 1 или 2;

Подсказка 3: Для любых других значений вам понадобится вернуть сумму через вызов fibonacci с number - 1 и number - 2.

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

Результатом выполнения данного кода будет:

Ключевые особенности функций в Kotlin

  • Функция используется для определения задачи, которую можно выполнять столько раз, сколько потребуется без необходимости повторно писать код;
  • Функции могут принимать ноль или более параметров и, при необходимости, возвращать значение;
  • Для ясности при вызове функции можно использовать именованные аргументы;
  • Указание значений функций по умолчанию может упростить работу и сократить объем кода;
  • Функции могут обладать одним и тем же названием с разными параметрами. Это называется перегрузкой;
  • Можно назначать функции переменным и передавать их другим функциям;
  • У функций может быть специальный возвращаемый тип Nothing, который сообщает Kotlin, что эта функция никогда не завершится;
  • Стремитесь создавать функции с четкими и понятными названиями;
  • Одна функция должна выполнять ОДНУ задачу которая соответствует её названию.

Что дальше?

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

5 3 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x