1. The function

1.1 Function Declaration

Functions in Kotlin are declared using the fun keyword:

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

1.2 Function Usage

Calling a function uses the traditional method:

val result = double(2)
Copy the code

Call member functions using dot notation:

Stream().read() // Create an instance of class Stream and call read()Copy the code

1.3 parameter

Function parameters are defined using Pascal notation, namely name: type. Use commas (,) to separate parameters. Each parameter must have an explicit type:

Fun powerOf(number: Int, exponent: Int): Int {/*... * /}Copy the code

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

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

1.4 Functions that return Unit

If a function does not return any useful values, its return type is Unit. Unit is a type that has only one value, Unit. This value does not need to be explicitly returned:

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

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

fun printHello(name: String?) { …… }
Copy the code

1.5 Single expression functions

When a function returns a single expression, omit the curly braces and specify the body of the code after the = symbol:

fun double(x: Int): Int = x * 2
Copy the code

It is optional to explicitly declare the return type when the return value type can be inferred by the compiler:

fun double(x: Int) = x * 2
Copy the code

2. Higher-order functions and lambda expressions

Kotlin functions are first-class, which means that they can be stored in variables and data structures, passed as arguments to other higher-order functions, and returned from other higher-order functions. Functions can be manipulated just like any other non-functional value.

2.1 Higher-order functions

Higher-order functions are functions that use functions as arguments or return values. A good example is the functional-style fold of a collection, which takes an initial cumulative value with a joiner function and builds the return value by continuously jointing the current cumulative value with each collection element into the cumulative value:

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
}
Copy the code

In the above code, the argument combine has the function type (R, T) -> R, so fold takes a function as an argument that takes two arguments of type R and T and returns a value of type R. The function is called inside the for-loop and its return value is assigned to accumulator. To call a fold, it needs to be passed an instance of the function type as an argument, and in higher-order function calls, the lambda expression (described in more detail below) is widely used for this purpose.

To call a fold, it needs to be passed an instance of the function type as an argument, and in higher-order function calls, the lambda expression (described in more detail below) is widely used for this purpose.

Val items = listOf(1, 2, 3, 4, 5) // Lambdas expressions are blocks of code enclosed in curly braces. Items.fold (0, {// If a lambda expression has arguments, preceded by arguments, followed by "->" acc: Int, I: Int -> print("acc = $acc, I = $I, ") val result = acc + I println("result = $result") Result}) // The argument type of the lambda expression is optional, if it can be inferred: Val joinedToString = Items.fold ("Elements:", {acc, I -> acc + "" + I})  val product = items.fold(1, Int::times)Copy the code

2.2 Function Types

Kotlin uses a series of function types like (Int) -> String to handle function declarations: val onClick: () -> Unit =… .

These types have a special notation corresponding to the function signature, that is, their arguments and return values:

  • All function types have a list of argument types enclosed in parentheses and a return type:(A, B) -> CIndicates that the accept types areABTwo arguments and return oneCThe function type of the type value. The parameter type list can be empty, as in() -> A.UnitThe return typeDo not omit.
  • Function types can have an extraThe receiverType, which specifies: type before the point in the notationA.(B) -> CWhich means that you canAOn the receiver objectBType parameter is called and returns oneCType value.The numeric face value of the letter with the recipientOften used in conjunction with these types.
  • Hang up functionA type of function that belongs to a special class and has one of the following representationssuspendModifier, for examplesuspend () -> Unitorsuspend A.(B) -> C.

The function type notation can optionally include the function’s argument name: (x: Int, y: Int) -> Point. These names can be used to indicate the meaning of the parameters.

To specify a nullable function type, use parentheses :((Int, Int) -> Int)? .

Function types can be joined with parentheses :(Int) -> ((Int) -> Unit)

The arrow notation is right-combined, (Int) -> (Int) -> Unit is equivalent to the previous example, but not equal to ((Int) -> (Int)) -> Unit.

You can also give a function type a nickname by using a type alias:

typealias ClickHandler = (Button, ClickEvent) -> Unit
Copy the code

2.3 Function type instantiation

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

  • A code block that uses a letter numeric face value takes one of the following forms:

    • Lambda expressions: { a, b -> a + b }.
    • Anonymous functions: fun(s: String): Int { return s.toIntOrNull() ?: 0 }

    A function numeric face value with a receiver can be used as a value of the function type with a receiver.

  • Use an already declared callable reference

    • Top-level, local, member, extensionfunction:::isOdd,String::toInt.
    • Top-level, members, extensionsattribute:List<Int>::size.
    • The constructor:::Regex
  • Using an instance of a custom class that implements an interface of a function type:

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

    If there is enough information, the compiler can infer the function type of the variable:

    Val a = {I: Int -> I + 1} val a = {I: Int -> I + 1

  • Function type nonliterals with and without a receiver are interchangeable, where the receiver can replace the first argument and vice versa. For example, values of type (A, B) -> C can be passed or assigned to places where A.(B) -> C is expected, and vice versa:

Function type nonliterals with and without a receiver are interchangeable, where the receiver can replace the first argument and vice versa. For example, values of type (A, B) -> C can be passed or assigned to places where A.(B) -> C is expected, and vice versa:

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

Copy the code

Note that the default is to infer a function type with no receiver, even if the variable is initialized with an extended function reference. If you want to change this, explicitly specify the variable type.

2.4 Lambda expressions and anonymous functions

Lambda expressions are anonymous functions at their core because they are implemented by anonymous functions in the underlying implementation. But we don’t have to worry about the underlying implementation when we use it. However, the emergence of Lambda does reduce the amount of code to write, and it also makes the code more concise. The syntax for Lambda, the foundation of functional programming, is also fairly simple. Here’s a simple code demonstration that doesn’t give you a sense of the brevity of Lambda expressions.

Ex. :

Mbtn.setonclicklistener (object: view.onClickListener {override fun onClick(v: View?)) { Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show() } })Copy the code

Is equivalent to

// call mbtn.setonClickListener {toast.maketext (this,"onClick", toast.length_short).show()}Copy the code

2.4.1 Lambda expressions and anonymous functions

Lambda expressions and anonymous functions are “functional-numeral values,” that is, functions that are not declared but are passed immediately as expressions. Consider the following example:

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

The function Max is a higher-order function that takes a function as its second argument. Its second argument is an expression that is itself a function, the function numeric value, which is equivalent to the following named function:

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