- Функции
- Использование функций
- Параметры
- Аргументы по умолчанию
- Именованные аргументы
- Функции с возвращаемым типом Unit
- Функции с одним выражением
- Явные типы возвращаемых значений
- Нефиксированное число аргументов (varargs)
- Инфиксная запись
- Область видимости функций
- Локальные функции
- Функции-члены
- Функции-обобщения
- Функции с хвостовой рекурсией
- How to Pass vararg to a varag function or constructor in Kotlin?
- Rafael Ferreira Rocha
- People also ask
- 1 Answers
Функции
В Kotlin функции объявляются с помощью ключевого слова fun .
Использование функций
При вызове функции используется традиционный подход:
Для вызова вложенной функции используется знак точки.
Stream().read() //создаёт экземпляр класса Stream и вызывает read()
Параметры
Параметры функции записываются аналогично системе обозначений в языке Pascal — имя: тип. Параметры разделены запятыми. Каждый параметр должен быть явно указан.
fun powerOf(number: Int, exponent: Int): Int < /*. */ >
Вы можете использовать завершающую запятую при объявлении параметров функции.
fun powerOf( number: Int, exponent: Int, // завершающая запятая ) < /*. */ >
Аргументы по умолчанию
Параметры функции могут иметь значения по умолчанию, которые используются в случае, если аргумент функции не указан при её вызове. Это позволяет снизить уровень перегруженности кода.
fun read( b: ByteArray, off: Int = 0, len: Int = b.size, ) < /*. */ >
Значения по умолчанию указываются после типа знаком = .
Переопределённые методы всегда используют те же самые значения по умолчанию, что и их базовые методы. При переопределении методов со значениями по умолчанию в сигнатуре эти параметры должны быть опущены.
open class A < open fun foo(i: Int = 10) < /*. */ >> class B : A() < override fun foo(i: Int) < /*. */ >// значение по умолчанию указать нельзя >
Если параметр по умолчанию предшествует параметру без значения по умолчанию, значение по умолчанию можно использовать только при вызове функции с именованными аргументами.
fun foo( bar: Int = 0, baz: Int, ) < /*. */ >foo(baz = 1) // Используется значение по умолчанию bar = 0
Но если последний аргумент после параметров по умолчанию — лямбда, вы можете передать её либо как именованный аргумент, либо за скобками.
fun foo( bar: Int = 0, baz: Int = 1, qux: () -> Unit, ) < /*. */ >foo(1) < println("hello") >// Используется значение по умолчанию baz = 1 foo(qux = < println("hello") >) // Используется оба значения по умолчанию: bar = 0 и baz = 1 foo < println("hello") >// Используется оба значения по умолчанию: bar = 0 и baz = 1
Именованные аргументы
При вызове функции вы можете явно указать имена одного или нескольких аргументов. Это может быть полезно, когда у функции большой список аргументов, и сложно связать значение с аргументом, особенно если это логическое или null значение.
При явном указывании имен аргументов в вызове функции, вы можете свободно изменять порядок их перечисления, и, если вы хотите использовать их значения по умолчанию, вы можете просто пропустить эти аргументы.
Рассмотрим следующую функцию reformat() , которая имеет 4 аргумента со значениями по умолчанию:
fun reformat( str: String, normalizeCase: Boolean = true, upperCaseFirstLetter: Boolean = true, divideByCamelHumps: Boolean = false, wordSeparator: Char = ' ', ) < /*. */ >
При её вызове, вам не нужно явно указывать все имена аргументов.
reformat( "String!", false, upperCaseFirstLetter = false, divideByCamelHumps = true, '_' )
Вы можете пропустить все аргументы со значением по умолчанию.
reformat("This is a long String!")
Вы также можете пропустить не только все аргументы со значениями по умолчанию, но и лишь некоторые из них. Однако после первого пропущенного аргумента вы должны указывать имена всех последующих аргументов.
reformat("This is a short String!", upperCaseFirstLetter = false, wordSeparator = '_')
Вы можете передать переменное количество аргументов ( vararg ) с именами, используя оператор spread .
fun foo(vararg strings: String) < /*. */ >foo(strings = *arrayOf("a", "b", "c"))
On the JVM: You can’t use the named argument syntax when calling Java functions because Java bytecode does not > always preserve the names of function parameters. —>
В JVM: синтаксис именованных аргументов не может быть использован при вызове Java функций, потому как байт-код Java не всегда сохраняет имена параметров функции.
Функции с возвращаемым типом Unit
Если функция не возвращает никакого полезного значения, её возвращаемый тип — Unit . Unit — тип только с одним значением — Unit . Это значение не нуждается в явном указании возвращения функции.
fun printHello(name: String?): Unit < if (name != null) println("Hello $name") else println("Hi there!") // `return Unit` или `return` необязательны >
Указание типа Unit в качестве возвращаемого значения тоже не является обязательным. Код, написанный выше, и следующий код совершенно идентичны:
fun printHello(name: String?) < /*. */ >
Функции с одним выражением
Когда функция возвращает одно единственное выражение, фигурные скобки < >могут быть опущены, и тело функции может быть описано после знака = .
fun double(x: Int): Int = x * 2
Явное объявление возвращаемого типа является необязательным, когда он может быть определен компилятором.
Явные типы возвращаемых значений
Функции с блочным телом всегда должны иметь явно указанный возвращаемый ими тип данных, если только они не предназначены для возврата Unit , тогда указание типа возвращаемого значения необязательно.
Kotlin самостоятельно не вычисляет тип возвращаемого значения для функций с блочным телом, потому что подобные функции могут иметь сложную структуру, и возвращаемый тип будет неочевидным для читающего этот код человека (иногда даже для компилятора).
Нефиксированное число аргументов (varargs)
Параметр функции (обычно для этого используется последний) может быть помечен модификатором vararg .
fun asList(vararg ts: T): List < val result = ArrayList() for (t in ts) // ts - это массив (Array) result.add(t) return result >
Это позволит указать несколько значений в качестве аргументов функции.
Внутри функции параметр с меткой vararg и типом T виден как массив элементов T , таким образом переменная ts в вышеуказанном примере имеет тип Array .
Только один параметр может быть помечен как vararg . Если параметр с именем vararg не стоит на последнем месте в списке аргументов, значения для последующих параметров могут быть переданы только с использованием синтаксиса именованных аргументов. В случае, если параметр является функцией, для этих целей можно вынести лямбду за фигурные скобки.
При вызове vararg -функции вы можете передать аргументы один за другим, например asList(1, 2, 3) , или, если у нас уже есть необходимый массив элементов и вы хотите передать его содержимое в функцию, используйте оператор spread (необходимо пометить массив знаком * ).
val a = arrayOf(1, 2, 3) val list = asList(-1, 0, *a, 4)
Если вы хотите передать массив примитивного типа в vararg , вам необходимо преобразовать его в обычный (типизированный) массив с помощью функции toTypedArray() .
val a = intArrayOf(1, 2, 3) // IntArray - массив примитивного типа val list = asList(-1, 0, *a.toTypedArray(), 4)
Инфиксная запись
Функции, помеченные ключевым словом infix , могут вызываться с использованием инфиксной записи (без точки и скобок для вызова). Инфиксные функции должны соответствовать следующим требованиям:
- Они должны являться членом другой функции или расширения;
- В них должен использоваться только один параметр;
- Параметр не должен принимать переменное количество аргументов и не должен иметь значения по умолчанию.
infix fun Int.shl(x: Int): Int < /*. */ >// вызов функции, с использованием инфиксной записи 1 shl 2 // то же самое, что 1.shl(2)
Infix function calls have lower precedence than arithmetic operators, type casts, and the `rangeTo` operator. > The following expressions are equivalent: > * `1 shl 2 + 3` is equivalent to `1 shl (2 + 3)` > * `0 until n * 2` is equivalent to `0 until (n * 2)` > * `xs union ys as Set ` is equivalent to `xs union (ys as Set )` > > On the other hand, an infix function call’s precedence is higher than that of the boolean operators `&&` and `||`, `is`- > and `in`-checks, and some other operators. These expressions are equivalent as well: > * `a && b xor c` is equivalent to `a && (b xor c)` > * `a xor b in c` is equivalent to `(a xor b) in c` —>
- 1 shl 2 + 3 эквивалентно 1 shl (2 + 3) ,
- 0 until n * 2 эквивалентно 0 until (n * 2) ,
- xs union ys as Set эквивалентно xs union (ys as Set) .
- a && b xor c эквивалентно a && (b xor c) ,
- a xor b in c эквивалентно (a xor b) in c .
Обратите внимание, что инфиксные функции всегда требуют указания как получателя, так и параметра. Когда вы вызываете метод на текущем приемнике, используя инфиксную запись, явно используйте this . Это необходимо для обеспечения однозначного синтаксического анализа.
class MyStringCollection < infix fun add(s: String) < /*. */ >fun build() < this add "abc" // Верно add("abc") // Верно //add "abc" // Не верно: получатель должен быть указан >>
Область видимости функций
В Kotlin функции могут быть объявлены в самом начале файла, что значит, что вам необязательно создавать класс, чтобы воспользоваться его функцией (как в Java, C# или Scala). В дополнение к этому, функции в Kotlin могут быть объявлены локально, как функции-члены и функции-расширения.
Локальные функции
Kotlin поддерживает локальные функции, т.е. функции, вложенные в другие функции.
fun dfs(graph: Graph) < fun dfs(current: Vertex, visited: MutableSet) < if (!visited.add(current)) return for (v in current.neighbors) dfs(v, visited) >dfs(graph.vertices[0], HashSet()) >
Локальная функция может иметь доступ к локальным переменным внешних по отношению к ним функций (типа closure). Таким образом, в примере, приведённом выше, visited может быть локальной переменной.
fun dfs(graph: Graph) < val visited = HashSet() fun dfs(current: Vertex) < if (!visited.add(current)) return for (v in current.neighbors) dfs(v) >dfs(graph.vertices[0]) >
Функции-члены
Функции-члены — это функции, объявленные внутри классов или объектов.
Функции-члены вызываются с использованием точки.
Sample().foo() // создаёт инстанс класса Sample и вызывает его функцию foo
Для более подробной информации о классах и их элементах см. Классы и Наследование.
Функции-обобщения
Функции могут иметь обобщённые параметры, которые задаются треугольными скобками и помещаются перед именем функции.
fun singletonList(item: T): List < /*. */ >
Для более подробной информации см. Обобщения.
Функции с хвостовой рекурсией
Kotlin поддерживает стиль функционального программирования, известный как «хвостовая рекурсия». Это позволяет использовать циклические алгоритмы вместо рекурсивных функции, но без риска переполнения стэка. Когда функция помечена модификатором tailrec и её форма отвечает требованиям компилятора, он оптимизирует рекурсию, оставляя вместо неё быстрое и эффективное решение этой задачи, основанное на циклах.
val eps = 1E-10 // этого достаточно, может быть 10^-15 tailrec fun findFixPoint(x: Double = 1.0): Double = if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))
Этот код высчитывает fixpoint косинуса, который является математической константой. Он просто-напросто постоянно вызывает Math.cos , начиная с 1.0 до тех пор, пока результат не изменится, приняв значение 0.7390851332151611 для заданной точности eps . Получившийся код эквивалентен вот этому более традиционному стилю:
val eps = 1E-10 // этого достаточно, может быть 10^-15 private fun findFixPoint(): Double < var x = 1.0 while (true) < val y = Math.cos(x) if (Math.abs(x - y) < eps) return x x = Math.cos(x) >>
Для соответствия требованиям модификатора tailrec , функция должна вызывать сама себя в качестве последней операции, которую она предпринимает. Вы не можете использовать хвостовую рекурсию, когда существует ещё какой-то код после вызова этой самой рекурсии. Также нельзя использовать её внутри блоков try / catch / finally или в open функциях. На данный момент хвостовая рекурсия поддерживается только в backend виртуальной машины Java (JVM) и в Kotlin/Native.
© 2015—2023 Open Source Community
How to Pass vararg to a varag function or constructor in Kotlin?
Kotlin does not allow my subclass to pass vararg to my super class constuctor Here is my Operation class:
package br.com.xp.operation import java.util.ArrayList abstract class Operation(vararg values: Value) : Value < protected var values: MutableList= ArrayList() internal abstract val operator: String >
and here is my SubtractionOperation class:
package br.com.xp.operation class SubtractionOperation private constructor(vararg values: Value) : Operation(values)
Type mismatch Required Value found Array
Can anyone explain why this is not possible?
asked Nov 27 '17 01:11
Rafael Ferreira Rocha
People also ask
In Kotlin, You can pass a variable number of arguments to a function by declaring the function with a vararg parameter. a vararg parameter of type T is internally represented as an array of type T ( Array ) inside the function body.
Kotlin Android. Sometimes we need a function where we can pass n number of parameters, and the value of n can be decided at runtime. Kotlin provides us to achieve the same by defining a parameter of a function as vararg .
1 Answers
Inside a function a vararg -parameter of type T is visible as an array of T .
So in the SubtractionOperation constructor, values is really an Array . You need to use the spread operator ( * ) to forward these on:
class SubtractionOperation private constructor(vararg values: Value) : Operation(*values) .