Kotlin vararg in constructor

Функции

В 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?

Rafael Ferreira Rocha Avatar

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) . 

Источник

Читайте также:  Javascript неизвестное количество аргументов
Оцените статью