Kotlin param

Kotlin param DEFAULT

Effective Kotlin Item 48: Use inline modifier for functions with parameters of functional types

You might have noticed that nearly all Kotlin stdlib higher-order functions have an inline modifier.

public inline fun repeat(times: Int, action: (Int) -> Unit) { for (index in 0 until times) { action(index) } } public inline fun <T, R> Iterable<T>.map( transform: (T) -> R ): List<R> { return mapTo( ArrayList<R>(collectionSizeOrDefault(10)), transform ) } public inline fun <T> Iterable<T>.filter( predicate: (T) -> Boolean ): List<T> { return filterTo(ArrayList<T>(), predicate) }

What this modifier does is that during compilation, all uses of this function are replaced with its body. Also, all calls of function arguments inside are replaced with those functions bodies. So the following function call:

repeat(10) { print(it) }

During compilation will be replaced with the following:

for (index in 0 until 10) { print(index) }

It is a significant change compared to how functions are executed normally. In a normal function, execution jumps into this function body, invokes all statements, then jumps back to the place where the function was invoked. Replacing calls with bodies is a significantly different behavior.

There are a few advantages to this behavior:

  1. A type argument can be reified
  2. Functions with functional parameters are faster when they are inline
  3. Non-local return is allowed

There are also some costs to using this modifier. Let’s review both all the advantages and costs of the modifier.

A type argument can be reified

Java does not have generics in older versions. They were added to the Java programming language in 2004 within version J2SE 5.0. They are still not present in the JVM bytecode though. Therefore, generic types are erased during compilation. For instance compiles to . This is why we cannot check if an object is . We can only check if it is a .

any is List<Int> // Error any is List<*> // OK

For the same reason, we cannot operate on a type argument:

fun <T> printTypeName() { print(T::class.simpleName) // ERROR }

We can overcome this limitation by making a function inline. Function calls are replaced with its body, so type parameters uses can be replaced with type arguments, by using the reified modifier:

inline fun <reified T> printTypeName() { print(T::class.simpleName) } // Usage printTypeName<Int>() // Int printTypeName<Char>() // Char printTypeName<String>() // String

During compilation, the body of replaces usages, and the type argument replaces the type parameter:

print(Int::class.simpleName) // Int print(Char::class.simpleName) // Char print(String::class.simpleName) // String

is a useful modifier. For instance, it is used in from the stdlib to filter only elements of a certain type:

class Worker class Manager val employees: List<Any> = listOf(Worker(), Manager(), Worker()) val workers: List<Worker> = employees.filterIsInstance<Worker>()

It is also used in many libraries and util functions we define ourselves. The below example presents a common implementation of that uses Gson library. It also presents how Koin library uses this kind of functions to simplify both dependency injection and module declaration.

inline fun <reified T : Any> String.fromJsonOrNull(): T? = try { gson.fromJson(json, T::class.java) } catch (e: JsonSyntaxException) { null } // usage val user: User? = userAsText.fromJsonOrNull() // Koin module declaration val myModule = module { single { Controller(get()) } // get is reified single { BusinessService() } // get is reified } // Koin injection val service: BusinessService by inject() // inject is reified

Functions with functional parameters are faster when they are inlined

To be more concrete, all functions are slightly faster when they are inlined. There is no need to jump with execution and to track the back-stack. This is why small functions that are used very often in the stdlib are often inlined:

inline fun print(message: Any?) { System.out.print(message) }

This difference is most likely insignificant when a function does not have any functional parameter though. This is the reason why IntelliJ gives such a warning:

To understand why, we first need to understand what the problem is with operating on functions as objects. These kinds of objects, created using function literals, need to be held somehow. In Kotlin/JS, it is simple since JavaScript treats functions as first-class citizens. Therefore in Kotlin/JS, it is either a function or a function reference. On Kotlin/JVM, some object needs to be created, either using a JVM anonymous class or a normal class. Therefore the following lambda expression:

val lambda: () -> Unit = { // code }

Will be compiled to a class. Either a JVM anonymous class:

Or it can be compiled into a normal class defined in a separate file:

There is no significant difference between those two options.

Notice that this function type translated to the type. This is what a function type with no arguments in JVM is compiled to. Functions with more arguments compile to , ,, etc.

  • compiles to
  • compiles to
  • compiles to
  • compiles to

All those interfaces are generated by the Kotlin compiler. You cannot use them explicitly in Kotlin though because they are generated on demand. We should use function types instead. Although knowing that function types are just interfaces opens your eyes to some new possibilities. You can, for instance, implement a function type.

class OnClickListener : () -> Unit { override fun invoke() { // ... } }

As illustrated in Item 47: Avoid unnecessary object creation, wrapping the body of a function into an object will slow down the code. This is why among the two functions below, the first one will be faster:

inline fun repeat(times: Int, action: (Int) -> Unit) { for (index in 0 until times) { action(index) } } fun repeatNoinline(times: Int, action: (Int) -> Unit) { for (index in 0 until times) { action(index) } }

The difference is visible, but rarely significant in real-life examples. Although if we design our test well, you can see this difference clearly:

@Benchmark fun nothingInline(blackhole: Blackhole) { repeat(100_000_000) { blackhole.consume(it) } } @Benchmark fun nothingNoninline(blackhole: Blackhole) { noinlineRepeat(100_000_000) { blackhole.consume(it) } }

The first one takes on my computer on average 189 ms. The second one takes on average 447 ms. This difference comes from the fact that in the first function we only iterate over numbers and call an empty function. In the second function, we call a method that iterates over numbers and calls an object, and this object calls an empty function. All that difference comes from the fact that we use an extra object (Item 47: Avoid unnecessary object creation).

To show a more typical example, let’s say that we have 5 000 products, and we need to sum up the prices of the ones that were bought. We can do it simply by:

users.filter { it.bought }.sumByDouble { it.price }

In my machine, it takes 38 ms to calculate on average. How much would it be if and functions were not inline? 42 ms on average on my machine. This doesn't look like a lot, but this is ~10% difference every time when you use methods for collection processing.

A more significant difference between inline and non-inline functions manifests itself when we capture local variables in function literals. A captured value needs to be wrapped as well into some object and whenever it is used, it needs to be done using this object. For instance, in the following code:

var l = 1L noinlineRepeat(100_000_000) { l += it }

A local variable cannot be used directly in non-inlines lambda. This is why during compilation time, the value of will be wrapped into a reference object:

val a = Ref.LongRef() a.element = 1L noinlineRepeat(100_000_000) { a.element = a.element + it }

This is a more significant difference because often, such objects might be used many times: every time we use a function created by a function literal. For instance, in the above example, we use two times. Therefore extra object use will happen 2 * 100 000 000. To see this difference, let’s compare the following functions:

@Benchmark // On average 30 ms fun nothingInline(blackhole: Blackhole) { var l = 0L repeat(100_000_000) { l += it } blackhole.consume(l) } @Benchmark // On average 274 ms fun nothingNoninline(blackhole: Blackhole) { var l = 0L noinlineRepeat(100_000_000) { l += it } blackhole.consume(l) }

The first one on my machine takes 30 ms. The second one 274 ms. This comes from the accumulated effects of the facts that the function is an object, and the local variable needs to be wrapped. Those makes tiny barriers, that needs to be passed many times, again and again. On the end, this makes a significant difference. Since in most cases we don’t know how functions with parameters of functional types will be used, when we define a utility function with such parameters, for instance for collection processing, it is a good practice making it inline. This is why most extension functions with parameters of functional types in the stdlib are .

Non-local return is allowed

Previously defined looks much like a control structure. Just compare it with a if expression or for-loop:

if (value != null) { print(value) } for (i in 1..10) { print(i) } repeatNoninline(10) { print(it) }

Although one significant difference is that return is not allowed inside:

fun main() { repeatNoinline(10) { print(it) return // ERROR: Not allowed } }

This is the result of what function literals are compiled to. We cannot return from , when our code is located in another class. There is no such limitation when a function literal is inlined. The code will be located in the function anyway.

fun main() { repeat(10) { print(it) return // OK } }

Thanks to that, functions can look and behave more like control structures:

fun getSomeMoney(): Money? { repeat(100) { val money = searchForMoney() if (money != null) return money } return null }

Costs of inline modifier

Inline is a useful modifier, but it should not be used everywhere. There are some cases where they should not be used. Let's review their most important limitations.

Inline functions cannot be recursive. Otherwise, they would replace their calls infinitely. Recurrent cycles are especially dangerous because, at the moment, it does not show an error in the IntelliJ:

inline fun a() { b() } inline fun b() { c() } inline fun c() { a() }

Inline functions cannot use elements with more restrictive visibility. We cannot use private or internal functions or properties in a public inline function. In fact, an inline function cannot use anything with more restrictive visibility:

internal inline fun read() { val reader = Reader() // Error // ... } private class Reader { // ... }

This is why they cannot be used to hide implementation, and so they are rarely used in classes.

Inline functions make our code grow. To see the scale, let’s say that I really like printing . I first defined the following function:

inline fun printThree() { print(3) }

I liked to call it 3 times, so I added this function:

inline fun threePrintThree() { printThree() printThree() printThree() }

Still not satisfied. I’ve defined the following functions:

inline fun threeThreePrintThree() { threePrintThree() threePrintThree() threePrintThree() } inline fun threeThreeThreePrintThree() { threeThreePrintThree() threeThreePrintThree() threeThreePrintThree() }

What are they all compiled to? First two are very readable:

inline fun printThree() { print(3) } inline fun threePrintThree() { print(3) print(3) print(3) }

Next two were compiled to the following functions:

inline fun threeThreePrintThree() { print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) } inline fun threeThreeThreePrintThree() { print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) print(3) }

This is an abstract example, but it shows a big problem with inline functions: code grows really quickly when we overuse them. I met with this problem in a real-life project. Having too many inline functions calling each other is dangerous because our code might start growing exponentially.

Crossinline and noinline

There are cases when we want to inline a function, but for some reason, we cannot inline all function type arguments. In such cases we can use the following modifiers:

  • - it means that the function should be inlined but non-local return is not allowed. We use it when this function is used in another scope where non-local return is not allowed, for instance in another lambda that is not inlined.

  • - it means that this argument should not be inlined at all. It is used mainly when we use this function as an argument to another function that is not inlined.

inline fun requestNewToken( hasToken: Boolean, crossinline onRefresh: () -> Unit, noinline onGenerate: () -> Unit ) { if (hasToken) { httpCall("get-token", onGenerate) // We must use // noinline to pass function as an argument to a // function that is not inlined } else { httpCall("refresh-token") { onRefresh() // We must use crossinline to // inline function in a context where // non-local return is not allowed onGenerate() } } } fun httpCall(url: String, callback: () -> Unit) { /*...*/ }

It is good to know what the meaning of both modifiers is, but we can live without remembering them as IntelliJ IDEA suggests them when they are needed:

Summary

The main cases where we use inline functions are:

  • Very often used functions, like .
  • Functions that need to have a reified type passed as a type argument, like .
  • When we define top-level functions with parameters of functional types. Especially helper functions, like collection processing functions (like , , , ), scope functions (like , , ), or top-level utility functions (like , , ).

We rarely use inline functions to define an API, and we should be careful with cases when one inline function calls some other inline functions. Remember that code growth accumulates.

The author:

Reviewers:

Sours: https://kt.academy/article/ek-inline-functions

Kotlin Functions, Default and Named Arguments, Varargs and Function Scopes

Kotlin Functions, Default and Named Arguments, Varargs and Function Scopes

Functions are the basic building block of any program. In this article, you’ll learn how to declare and call functions in Kotlin. You’ll also learn about Function scopes, Default arguments, Named Arguments, and Varargs.

Defining and Calling Functions

You can declare a function in Kotlin using the keyword. Following is a simple function that calculates the average of two numbers -

Calling a function is simple. You just need to pass the required number of parameters in the function name like this -

Following is the general syntax of declaring a function in Kotlin.

Every function declaration has a function name, a list of comma-separated parameters, an optional return type, and a method body. The function parameters must be explicitly typed.

Single Expression Functions

You can omit the return type and the curly braces if the function returns a single expression. The return type is inferred by the compiler from the expression -

Note that, Unlike other statically typed languages like Scala, Kotlin does not infer return types for functions with block bodies. Therefore, Functions with block body must always specify return types explicitly.

Unit returning Functions

Functions which don’t return anything has a return type of . The type corresponds to in Java.

Note that, the type declaration is completely optional. So you can also write the above function declaration like this -

Function Default Arguments

Kotlin supports default arguments in function declarations. You can specify a default value for a function parameter. The default value is used when the corresponding argument is omitted from the function call.

Consider the following function for example -

If you call the above function with two arguments, it works just like any other function and uses the values passed in the arguments -

However, If you omit the argument that has a default value from the function call, then the default value is used in the function body -

If the function declaration has a default parameter preceding a non-default parameter, then the default value cannot be used while calling the function with position-based arguments.

Consider the following function -

While calling the above function, you can not omit the argument from the function call and selectively pass a value for the non-default parameter -

When you call a function with position-based arguments, the first argument corresponds to the first parameter, the second argument corresponds to the second parameter, and so on.

So for passing a value for the 2nd parameter, you need to specify a value for the first parameter as well -

However, The above use-case of selectively passing a value for a parameter is solved by another feature of Kotlin called Named Arguments.

Function Named Arguments

Kotlin allows you to specify the names of arguments that you’re passing to the function. This makes the function calls more readable. It also allows you to pass the value of a parameter selectively if other parameters have default values.

Consider the following function that we defined in the previous section -

You can specify the names of arguments while calling the function like this -

The above function call will use the default values for parameters and .

Similarly, you can call the function with all the parameters like this -

You can also reorder the arguments if you’re specifying the names -

You can use a mix of named arguments and position-based arguments as long as all the position-based arguments are placed before the named arguments -

The following function call is not allowed since it contains position-based arguments after named arguments -

Variable Number of Arguments (Varargs)

You can pass a variable number of arguments to a function by declaring the function with a parameter.

Consider the following function which accepts a vararg of numbers -

You can call the above function with any number of arguments -

In Kotlin, a parameter of type is internally represented as an array of type () inside the function body.

A function may have only one parameter. If there are other parameters following the parameter, then the values for those parameters can be passed using the named argument syntax -

Spread Operator

Usually, we pass the arguments to a function one-by-one. But if you already have an array and want to pass the elements of the array to the function, then you can use the spread operator like this -

Function Scope

Kotlin supports functional programming. Functions are first-class citizens in the language.

Unlike Java where every function needs to be encapsulated inside a class, Kotlin functions can be defined at the top level in a source file.

In addition to top-level functions, you also have the ability to define member functions, local functions, and extension functions.

1. Top Level Functions

Top level functions in Kotlin are defined in a source file outside of any class. They are also called package level functions because they are a member of the package in which they are defined.

The method itself is a top-level function in Kotlin since it is defined outside of any class.

Let’s now see an example of a top-level function. Check out the following function which is defined inside a package named -

You can access the above function directly inside the package -

However, If you want to call the function from other packages, then you need to import it as in the following example -

2. Member Functions

Member functions are functions which are defined inside a class or an object.

Member functions are called on the objects of the class using the (.) notation -

3. Local/Nested Functions

Kotlin allows you to nest function definitions. These nested functions are called Local functions. Local functions bring more encapsulation and readability to your program -

Local functions can access local variables of the outer function. So the above function is equivalent to the following -

Conclusion

Congratulations folks! In this article, you learned how to define and call functions in Kotlin, how to use default and named arguments, how to define and call functions with a variable number of arguments, and how to define top-level functions, member functions and local/nested functions

In future articles, I’ll write about extension functions, higher order functions and lambdas. So Stay tuned!

As always, Thank you for reading. Happy Kotlin Koding :)

Sours: https://www.callicoder.com/kotlin-functions/
  1. Street glide dimensions
  2. Maria bello ncis
  3. 2006 toyota tundra aftermarket grill

Annotations

Annotations are means of attaching metadata to code. To declare an annotation, put the modifier in front of a class:

annotation class Fancy

Additional attributes of the annotation can be specified by annotating the annotation class with meta-annotations:

  • specifies the possible kinds of elements which can be annotated with the annotation (such as classes, functions, properties, and expressions);

  • specifies whether the annotation is stored in the compiled class files and whether it's visible through reflection at runtime (by default, both are true);

  • allows using the same annotation on a single element multiple times;

  • specifies that the annotation is part of the public API and should be included in the class or method signature shown in the generated API documentation.

@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION) @Retention(AnnotationRetention.SOURCE) @MustBeDocumented annotation class Fancy

Usage

@Fancy class Foo { @Fancy fun baz(@Fancy foo: Int): Int { return (@Fancy 1) } }

If you need to annotate the primary constructor of a class, you need to add the keyword to the constructor declaration, and add the annotations before it:

class Foo @Inject constructor(dependency: MyDependency) { ... }

You can also annotate property accessors:

class Foo { var x: MyDependency? = null @Inject set }

Constructors

Annotations can have constructors that take parameters.

annotation class Special(val why: String) @Special("example") class Foo {}

Allowed parameter types are:

  • Types that correspond to Java primitive types (Int, Long etc.)

  • Strings

  • Classes ()

  • Enums

  • Other annotations

  • Arrays of the types listed above

Annotation parameters cannot have nullable types, because the JVM does not support storing as a value of an annotation attribute.

If an annotation is used as a parameter of another annotation, its name is not prefixed with the character:

annotation class ReplaceWith(val expression: String) annotation class Deprecated( val message: String, val replaceWith: ReplaceWith = ReplaceWith("")) @Deprecated("This function is deprecated, use === instead", ReplaceWith("this === other"))

If you need to specify a class as an argument of an annotation, use a Kotlin class (KClass). The Kotlin compiler will automatically convert it to a Java class, so that the Java code can access the annotations and arguments normally.

import kotlin.reflect.KClass annotation class Ann(val arg1: KClass<*>, val arg2: KClass<out Any>) @Ann(String::class, Int::class) class MyClass

Lambdas

Annotations can also be used on lambdas. They will be applied to the method into which the body of the lambda is generated. This is useful for frameworks like Quasar, which uses annotations for concurrency control.

annotation class Suspendable val f = @Suspendable { Fiber.sleep(10) }

Annotation use-site targets

When you're annotating a property or a primary constructor parameter, there are multiple Java elements which are generated from the corresponding Kotlin element, and therefore multiple possible locations for the annotation in the generated Java bytecode. To specify how exactly the annotation should be generated, use the following syntax:

class Example(@field:Ann val foo, // annotate Java field @get:Ann val bar, // annotate Java getter @param:Ann val quux) // annotate Java constructor parameter

The same syntax can be used to annotate the entire file. To do this, put an annotation with the target at the top level of a file, before the package directive or before all imports if the file is in the default package:

@file:JvmName("Foo") package org.jetbrains.demo

If you have multiple annotations with the same target, you can avoid repeating the target by adding brackets after the target and putting all the annotations inside the brackets:

class Example { @set:[Inject VisibleForTesting] var collaborator: Collaborator }

The full list of supported use-site targets is:

  • (annotations with this target are not visible to Java)

  • (property getter)

  • (property setter)

  • (receiver parameter of an extension function or property)

  • (constructor parameter)

  • (property setter parameter)

  • (the field storing the delegate instance for a delegated property)

To annotate the receiver parameter of an extension function, use the following syntax:

fun @receiver:Fancy String.myExtension() { ... }

If you don't specify a use-site target, the target is chosen according to the annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following list is used:

Java annotations

Java annotations are 100% compatible with Kotlin:

import org.junit.Test import org.junit.Assert.* import org.junit.Rule import org.junit.rules.* class Tests { // apply @Rule annotation to property getter @get:Rule val tempFolder = TemporaryFolder() @Test fun simple() { val f = tempFolder.newFile() assertEquals(42, getTheAnswer()) } }

Since the order of parameters for an annotation written in Java is not defined, you can't use a regular function call syntax for passing the arguments. Instead, you need to use the named argument syntax:

// Java public @interface Ann { int intValue(); String stringValue(); }

// Kotlin @Ann(intValue = 1, stringValue = "abc") class C

Just like in Java, a special case is the parameter; its value can be specified without an explicit name:

// Java public @interface AnnWithValue { String value(); }

// Kotlin @AnnWithValue("abc") class C

Arrays as annotation parameters

If the argument in Java has an array type, it becomes a parameter in Kotlin:

// Java public @interface AnnWithArrayValue { String[] value(); }

// Kotlin @AnnWithArrayValue("abc", "foo", "bar") class C

For other arguments that have an array type, you need to use the array literal syntax or :

// Java public @interface AnnWithArrayMethod { String[] names(); }

@AnnWithArrayMethod(names = ["abc", "foo", "bar"]) class C

Accessing properties of an annotation instance

Values of an annotation instance are exposed as properties to Kotlin code:

// Java public @interface Ann { int value(); }

// Kotlin fun foo(ann: Ann) { val i = ann.value }

Last modified: 11 February 2021

CoroutinesDestructuring declarations

Sours: https://kotlinlang.org/docs/annotations.html
Kotlin Android Tutorial - Learn How to Build an Android App 📱 7+ hours FREE Development Masterclass

High-order functions and lambdas

Kotlin functions are first-class, which means they can be stored in variables and data structures, and can be passed as arguments to and returned from other higher-order functions. You can perform any operations on functions that are possible for other non-function values.

To facilitate this, Kotlin, as a statically typed programming language, uses a family of function types to represent functions, and provides a set of specialized language constructs, such as lambda expressions.

Higher-order functions

A higher-order function is a function that takes functions as parameters, or returns a function.

A good example of a higher-order function is the functional programming idiom for collections. It takes an initial accumulator value and a combining function and builds its return value by consecutively combining the current accumulator value with each collection element, replacing the accumulator value each time:

fun <T, R> Collection<T>.fold( initial: R, combine: (acc: R, nextElement: T) -> R ): R { var accumulator: R = initial for (element: T in this) { accumulator = combine(accumulator, element) } return accumulator }

In the code above, the parameter has the function type, so it accepts a function that takes two arguments of types and and returns a value of type . It is invoked inside the loop, and the return value is then assigned to .

To call , you need to pass an instance of the function type to it as an argument, and lambda expressions (described in more detail below) are widely used for this purpose at higher-order function call sites:

fun main() { //sampleStart val items = listOf(1, 2, 3, 4, 5) // Lambdas are code blocks enclosed in curly braces. items.fold(0, { // When a lambda has parameters, they go first, followed by '->' acc: Int, i: Int -> print("acc = $acc, i = $i, ") val result = acc + i println("result = $result") // The last expression in a lambda is considered the return value: result }) // Parameter types in a lambda are optional if they can be inferred: val joinedToString = items.fold("Elements:", { acc, i -> acc + " " + i }) // Function references can also be used for higher-order function calls: val product = items.fold(1, Int::times) //sampleEnd println("joinedToString = $joinedToString") println("product = $product") }

Function types

Kotlin uses function types, such as , for declarations that deal with functions: .

These types have a special notation that corresponds to the signatures of the functions - their parameters and return values:

  • All function types have a parenthesized list of parameter types and a return type: denotes a type that represents functions that take two arguments of types and and return a value of type . The list of parameter types may be empty, as in . The return type cannot be omitted.

  • Function types can optionally have an additional receiver type, which is specified before the dot in the notation: the type represents functions that can be called on a receiver object with a parameter and return a value . Function literals with receiver are often used along with these types.

  • Suspending functions belong to a special kind of function type that have a suspend modifier in their notation, such as or .

The function type notation can optionally include names for the function parameters: . These names can be used for documenting the meaning of the parameters.

To specify that a function type is nullable, use parentheses as follows: .

Function types can also be combined using parentheses: .

The arrow notation is right-associative, is equivalent to the previous example, but not to .

You can also give a function type an alternative name by using a type alias:

typealias ClickHandler = (Button, ClickEvent) -> Unit

Instantiating a function type

There are several ways to obtain an instance of a function type:

  • Use a code block within a function literal, in one of the following forms:

    Function literals with receiver can be used as values of function types with receiver.

  • Use a callable reference to an existing declaration:

    • a top-level, local, member, or extension function: , ,

    • a top-level, member, or extension property: ,

    • a constructor:

    These include bound callable references that point to a member of a particular instance: .

  • Use instances of a custom class that implements a function type as an interface:

class IntTransformer: (Int) -> Int { override operator fun invoke(x: Int): Int = TODO() } val intFunction: (Int) -> Int = IntTransformer()

The compiler can infer the function types for variables if there is enough information:

val a = { i: Int -> i + 1 } // The inferred type is (Int) -> Int

Non-literal values of function types with and without a receiver are interchangeable, so the receiver can stand in for the first parameter, and vice versa. For instance, a value of type can be passed or assigned where a value of type is expected, and the other way around:

fun main() { //sampleStart val repeatFun: String.(Int) -> String = { times -> this.repeat(times) } val twoParameters: (String, Int) -> String = repeatFun // OK fun runTransformation(f: (String, Int) -> String): String { return f("hello", 3) } val result = runTransformation(repeatFun) // OK //sampleEnd println("result = $result") }

A function type with no receiver is inferred by default, even if a variable is initialized with a reference to an extension function. To alter that, specify the variable type explicitly.

Invoking a function type instance

A value of a function type can be invoked by using its operator: or just .

If the value has a receiver type, the receiver object should be passed as the first argument. Another way to invoke a value of a function type with receiver is to prepend it with the receiver object, as if the value were an extension function: .

Example:

fun main() { //sampleStart val stringPlus: (String, String) -> String = String::plus val intPlus: Int.(Int) -> Int = Int::plus println(stringPlus.invoke("<-", "->")) println(stringPlus("Hello, ", "world!")) println(intPlus.invoke(1, 1)) println(intPlus(1, 2)) println(2.intPlus(3)) // extension-like call //sampleEnd }

Inline functions

Sometimes it is beneficial to use inline functions, which provide flexible control flow, for higher-order functions.

Lambda expressions and anonymous functions

Lambda expressions and anonymous functions are function literals. Function literals are functions that are not declared but are passed immediately as an expression. Consider the following example:

max(strings, { a, b -> a.length < b.length })

The function is a higher-order function, as it takes a function value as its second argument. This second argument is an expression that is itself a function, called a function literal, which is equivalent to the following named function:

fun compare(a: String, b: String): Boolean = a.length < b.length

Lambda expression syntax

The full syntactic form of lambda expressions is as follows:

val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }

  • A lambda expression is always surrounded by curly braces.

  • Parameter declarations in the full syntactic form go inside curly braces and have optional type annotations.

  • The body goes after the .

  • If the inferred return type of the lambda is not , the last (or possibly single) expression inside the lambda body is treated as the return value.

If you leave all the optional annotations out, what's left looks like this:

val sum = { x: Int, y: Int -> x + y }

Passing trailing lambdas

According to Kotlin convention, if the last parameter of a function is a function, then a lambda expression passed as the corresponding argument can be placed outside the parentheses:

val product = items.fold(1) { acc, e -> acc * e }

Such syntax is also known as trailing lambda.

If the lambda is the only argument in that call, the parentheses can be omitted entirely:

run { println("...") }

it: implicit name of a single parameter

It's very common for a lambda expression to have only one parameter.

If the compiler can parse the signature without any parameters, the parameter does not need to be declared and can be omitted. The parameter will be implicitly declared under the name :

ints.filter { it > 0 } // this literal is of type '(it: Int) -> Boolean'

Returning a value from a lambda expression

You can explicitly return a value from the lambda using the qualified return syntax. Otherwise, the value of the last expression is implicitly returned.

Therefore, the two following snippets are equivalent:

ints.filter { val shouldFilter = it > 0 shouldFilter } ints.filter { val shouldFilter = it > 0 [email protected] shouldFilter }

This convention, along with passing a lambda expression outside of parentheses, allows for LINQ-style code:

strings.filter { it.length == 5 }.sortedBy { it }.map { it.uppercase() }

Underscore for unused variables

If the lambda parameter is unused, you can place an underscore instead of its name:

map.forEach { _, value -> println("$value!") }

Destructuring in lambdas

Destructuring in lambdas is described as a part of destructuring declarations.

Anonymous functions

The lambda expression syntax above is missing one thing – the ability to specify the function’s return type. In most cases, this is unnecessary because the return type can be inferred automatically. However, if you do need to specify it explicitly, you can use an alternative syntax: an anonymous function.

fun(x: Int, y: Int): Int = x + y

An anonymous function looks very much like a regular function declaration, except its name is omitted. Its body can be either an expression (as shown above) or a block:

fun(x: Int, y: Int): Int { return x + y }

The parameters and the return type are specified in the same way as for regular functions, except the parameter types can be omitted if they can be inferred from the context:

ints.filter(fun(item) = item > 0)

The return type inference for anonymous functions works just like for normal functions: the return type is inferred automatically for anonymous functions with an expression body, but it has to be specified explicitly (or is assumed to be ) for anonymous functions with a block body.

When passing anonymous functions as parameters, place them inside the parentheses. The shorthand syntax that allows you to leave the function outside the parentheses works only for lambda expressions.

Another difference between lambda expressions and anonymous functions is the behavior of non-local returns. A statement without a label always returns from the function declared with the keyword. This means that a inside a lambda expression will return from the enclosing function, whereas a inside an anonymous function will return from the anonymous function itself.

Closures

A lambda expression or anonymous function (as well as a local function and an object expression) can access its closure, which includes the variables declared in the outer scope. The variables captured in the closure can be modified in the lambda:

var sum = 0 ints.filter { it > 0 }.forEach { sum += it } print(sum)

Function literals with receiver

Function types with receiver, such as , can be instantiated with a special form of function literals – function literals with receiver.

As mentioned above, Kotlin provides the ability to call an instance of a function type with receiver while providing the receiver object.

Inside the body of the function literal, the receiver object passed to a call becomes an implicit, so that you can access the members of that receiver object without any additional qualifiers, or access the receiver object using a expression.

This behavior is similar to that of extension functions, which also allow you to access the members of the receiver object inside the function body.

Here is an example of a function literal with receiver along with its type, where is called on the receiver object:

val sum: Int.(Int) -> Int = { other -> plus(other) }

The anonymous function syntax allows you to specify the receiver type of a function literal directly. This can be useful if you need to declare a variable of a function type with receiver, and then to use it later.

val sum = fun Int.(other: Int): Int = this + other

Lambda expressions can be used as function literals with receiver when the receiver type can be inferred from the context. One of the most important examples of their usage is type-safe builders:

class HTML { fun body() { ... } } fun html(init: HTML.() -> Unit): HTML { val html = HTML() // create the receiver object html.init() // pass the receiver object to the lambda return html } html { // lambda with receiver begins here body() // calling a method on the receiver object }

Last modified: 07 October 2021

FunctionsInline functions

Sours: https://kotlinlang.org/docs/lambdas.html

Param kotlin

Kotlin Default and Named Arguments

Kotlin Default Argument

In Kotlin, you can provide default values to parameters in function definition.

If the function is called with arguments passed, those arguments are used as parameters. However, if the function is called without passing argument(s), default arguments are used.


How default arguments works?

Case I: All arguments passed


Both arguments passed to the function

The function takes two arguments. The arguments are provided with default values. However, is called by passing both arguments in the above program. Hence, the default arguments are not used.

The value of and will be and respectively inside the function.

Case II: All arguments are not passed


All arguments are not passed to the function

Here, only one (first) argument is passed to the function. Hence, the first argument uses the value passed to the function. However, second argument will take the default value since the second argument is not passed during function call.

The value of and will be and respectively inside the function.

Case III: No argument is passed


No arguments passed to the function

Here, the function is called without passing any argument. Hence, both arguments uses its default values.

The value of and will be and respectively inside the function.


Example: Kotlin Default Argument

When you run the program, the output will be:


Kotlin named argument

Before talking about named argument, let us consider a little modification of the above code:

Here, we are trying to pass second argument to the function, and use default argument for first argument. However, this code will give use an error. It's because the compiler thinks we are trying to provide 5 ( value) to character ( type).

To solve this situation, named arguments can be used. Here' how:


Example: Kotlin named argument

When you run the program, the output will be:

In the above program, we are using named argument () specifying that the parameter in the function definition should take this value (doesn't matter the position of the argument).

Named Arguments in Kotlin

The first argument uses the default value in the program.

Sours: https://www.programiz.com/kotlin-programming/default-named-arguments
Kotlin Android Tutorial - Learn How to Build an Android App 📱 7+ hours FREE Development Masterclass

Variable number of arguments (vararg) : Kotlin


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 . We can pass n number of parameters to a variable of the defined datatype or even of a generic type.

Let me give you an example.

We need a function which takes n number as inputs and returns the average of all the inputs. If the size of n variables is not fixed, we usually convert it into an array or list and pass it to function.


fun getAverage(numbersList: List

Now, what if the function itself takes n inputs and we don’t need to convert it into an array.


fun getAverage(vararg input: Int): Float {
var sum = 0.0f
for (item in input) {
sum += item
}
return (sum / input.size)
}

val result1 = getAverage(1, 2, 3)
val result2 = getAverage(1, 2, 3, 4, 5)

We can pass n number of inputs to the same function and can get the result back. Inside the function, the parameter is available as an Array object and we can perform all normal array operations on it.

We can even use type for .


fun <T> asList(vararg input: T): List<T> {
val result = ArrayList<T>()
for (item in input) // input is an Array
result.add(item)
return result
}

Only one parameter may be marked as . If a parameter is not the last one in the list, values for the following parameters can be passed using the named argument syntax, or if the parameter has a function type, the value can be passed by passing a lambda outside parentheses.

We can even pass all the elements of an array along with other arguments to a variable. We can use the spread () operator to do the same.


fun getAverage(vararg input: Int): Float {
var sum = 0.0f
for (item in input) {
sum += item
}
return (sum / input.size)
}
val array = intArrayOf(1, 2, 3, 4)
val result = getAverage(1, 2, 3, *array)



Reference: Kotlin docs

Sours: https://agrawalsuneet.github.io/blogs/variable-number-of-arguments-vararg-kotlin/

You will also be interested:

Functions

Kotlin functions are declared using the keyword:

fun double(x: Int): Int { return 2 * x }

Function usage

Functions are called using the standard approach:

val result = double(2)

Calling member functions uses dot notation:

Stream().read() // create instance of class Stream and call read()

Parameters

Function parameters are defined using Pascal notation - name: type. Parameters are separated using commas, and each parameter must be explicitly typed:

fun powerOf(number: Int, exponent: Int): Int { /*...*/ }

You can use a trailing comma when you declare function parameters:

fun powerOf( number: Int, exponent: Int, // trailing comma ) { /*...*/ }

Default arguments

Function parameters can have default values, which are used when you skip the corresponding argument. This reduces the number of overloads:

fun read( b: ByteArray, off: Int = 0, len: Int = b.size, ) { /*...*/ }

A default value is defined using after the type.

Overriding methods always use the same default parameter values as the base method. When overriding a method that has default parameter values, the default parameter values must be omitted from the signature:

open class A { open fun foo(i: Int = 10) { /*...*/ } } class B : A() { override fun foo(i: Int) { /*...*/ } // No default value is allowed. }

If a default parameter precedes a parameter with no default value, the default value can only be used by calling the function with named arguments:

fun foo( bar: Int = 0, baz: Int, ) { /*...*/ } foo(baz = 1) // The default value bar = 0 is used

If the last argument after default parameters is a lambda, you can pass it either as a named argument or outside the parentheses:

fun foo( bar: Int = 0, baz: Int = 1, qux: () -> Unit, ) { /*...*/ } foo(1) { println("hello") } // Uses the default value baz = 1 foo(qux = { println("hello") }) // Uses both default values bar = 0 and baz = 1 foo { println("hello") } // Uses both default values bar = 0 and baz = 1

Named arguments

When calling a function, you can name one or more of its arguments. This can be helpful when a function has many arguments and it's difficult to associate a value with an argument, especially if it's a boolean or value.

When you use named arguments in a function call, you can freely change the order they are listed in, and if you want to use their default values, you can just leave these arguments out altogether.

Consider the following function, , which has 4 arguments with default values.

fun reformat( str: String, normalizeCase: Boolean = true, upperCaseFirstLetter: Boolean = true, divideByCamelHumps: Boolean = false, wordSeparator: Char = ' ', ) { /*...*/ }

When calling this function, you don’t have to name all its arguments:

reformat( "String!", false, upperCaseFirstLetter = false, divideByCamelHumps = true, '_' )

You can skip all the ones with default values:

reformat("This is a long String!")

You are also able to skip specific arguments with default values, rather than omitting them all. However, after the first skipped argument, you must name all subsequent arguments:

reformat("This is a short String!", upperCaseFirstLetter = false, wordSeparator = '_')

You can pass a variable number of arguments () with names using the operator:

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.

Unit-returning functions

If a function does not return a useful value, its return type is . is a type with only one value - . This value does not have to be returned explicitly:

fun printHello(name: String?): Unit { if (name != null) println("Hello $name") else println("Hi there!") // `return Unit` or `return` is optional }

The return type declaration is also optional. The above code is equivalent to:

fun printHello(name: String?) { ... }

Single-expression functions

When a function returns a single expression, the curly braces can be omitted and the body is specified after a symbol:

fun double(x: Int): Int = x * 2

Explicitly declaring the return type is optional when this can be inferred by the compiler:

fun double(x: Int) = x * 2

Explicit return types

Functions with block body must always specify return types explicitly, unless it's intended for them to return , in which case specifying the return type is optional.

Kotlin does not infer return types for functions with block bodies because such functions may have complex control flow in the body, and the return type will be non-obvious to the reader (and sometimes even for the compiler).

Variable number of arguments (varargs)

You can mark a parameter of a function (usually the last one) with the modifier:

fun <T> asList(vararg ts: T): List<T> { val result = ArrayList<T>() for (t in ts) // ts is an Array result.add(t) return result }

In this case, you can pass a variable number of arguments to the function:

val list = asList(1, 2, 3)

Inside a function, a -parameter of type is visible as an array of , as in the example above, where the variable has type .

Only one parameter can be marked as . If a parameter is not the last one in the list, values for the subsequent parameters can be passed using named argument syntax, or, if the parameter has a function type, by passing a lambda outside the parentheses.

When you call a -function, you can pass arguments individually, for example . If you already have an array and want to pass its contents to the function, use the spread operator (prefix the array with ):

val a = arrayOf(1, 2, 3) val list = asList(-1, 0, *a, 4)

If you want to pass a primitive type array into , you need to convert it to a regular (typed) array using the function:

val a = intArrayOf(1, 2, 3) // IntArray is a primitive type array val list = asList(-1, 0, *a.toTypedArray(), 4)

Infix notation

Functions marked with the keyword can also be called using the infix notation (omitting the dot and the parentheses for the call). Infix functions must meet the following requirements:

infix fun Int.shl(x: Int): Int { ... } // calling the function using the infix notation 1 shl 2 // is the same as 1.shl(2)

Infix function calls have lower precedence than arithmetic operators, type casts, and the operator. The following expressions are equivalent:

  • is equivalent to

  • is equivalent to

  • is equivalent to

On the other hand, an infix function call's precedence is higher than that of the boolean operators and , - and -checks, and some other operators. These expressions are equivalent as well:

  • is equivalent to

  • is equivalent to

Note that infix functions always require both the receiver and the parameter to be specified. When you're calling a method on the current receiver using the infix notation, use explicitly. This is required to ensure unambiguous parsing.

class MyStringCollection { infix fun add(s: String) { /*...*/ } fun build() { this add "abc" // Correct add("abc") // Correct //add "abc" // Incorrect: the receiver must be specified } }

Function scope

Kotlin functions can be declared at the top level in a file, meaning you do not need to create a class to hold a function, which you are required to do in languages such as Java, C#, and Scala. In addition to top level functions, Kotlin functions can also be declared locally as member functions and extension functions.

Local functions

Kotlin supports local functions, which are functions inside other functions:

fun dfs(graph: Graph) { fun dfs(current: Vertex, visited: MutableSet<Vertex>) { if (!visited.add(current)) return for (v in current.neighbors) dfs(v, visited) } dfs(graph.vertices[0], HashSet()) }

A local function can access local variables of outer functions (the closure). In the case above, can be a local variable:

fun dfs(graph: Graph) { val visited = HashSet<Vertex>() fun dfs(current: Vertex) { if (!visited.add(current)) return for (v in current.neighbors) dfs(v) } dfs(graph.vertices[0]) }

Member functions

A member function is a function that is defined inside a class or object:

class Sample { fun foo() { print("Foo") } }

Member functions are called with dot notation:

Sample().foo() // creates instance of class Sample and calls foo

For more information on classes and overriding members see Classes and Inheritance.

Generic functions

Functions can have generic parameters, which are specified using angle brackets before the function name:

fun <T> singletonList(item: T): List<T> { /*...*/ }

For more information on generic functions, see Generics.

Tail recursive functions

Kotlin supports a style of functional programming known as tail recursion. For some algorithms that would normally use loops, you can use a recursive function instead without the risk of stack overflow. When a function is marked with the modifier and meets the required formal conditions, the compiler optimizes out the recursion, leaving behind a fast and efficient loop based version instead:

val eps = 1E-10 // "good enough", could be 10^-15 tailrec fun findFixPoint(x: Double = 1.0): Double = if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))

This code calculates the of cosine, which is a mathematical constant. It simply calls repeatedly starting at until the result no longer changes, yielding a result of for the specified precision. The resulting code is equivalent to this more traditional style:

val eps = 1E-10 // "good enough", could be 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) } }

To be eligible for the modifier, a function must call itself as the last operation it performs. You cannot use tail recursion when there is more code after the recursive call, and you cannot use it within / / blocks. Currently, tail recursion is supported by Kotlin for the JVM and Kotlin/Native.

See also:

Last modified: 04 October 2021

Type aliasesHigh-order functions and lambdas

Sours: https://kotlinlang.org/docs/functions.html


1 2 3