Kotlin learning: Functions

Kotlin learning (VII): function

Basic usage of function

The Kotlin function must start with the fun keyword, followed by the function name and a pair of parentheses. In the parentheses is the function parameter list. If the function has a return value, add a colon (:) after the parentheses, followed by the return value type of the function.

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

The calling function uses traditional methods:

val result = double(2)

Call member functions using point notation:

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

Calling functions using infix notation

Kotlin allows functions to be called using infix expressions. Infix expression means putting the name of a function between two operands. These two operands are the object or value containing the function on the left and the parameter value of the function on the right. As can be seen from this description, not all functions support infix expressions. Functions that support infix tagging must meet the following three conditions.

  • Member function or extension function.
  • There is only one parameter.
  • Declare the function using the infix keyword.

Here is an example. In this example, use the extension to add a division operation to the String class. What is a String division operation? The String division here is actually a syntax sugar, which is to remove all denominator strings contained in the numerator String

infix fun String.div(str: String): String {
   return this.replace(str, "")
}

If you call it in a normal way, you can call it through the following method

fun main(args: Array<String>) {
    var str = "Hello world"
    println(str.div("l")) // Output: Heo word
}

If infix expression is used to call, the writing method is as follows:

fun main(args: Array<String>) {
    var str = "Hello world"
    println(str div "l") // Output: Heo word
}

Infix expressions can also be used continuously

fun main(args: Array<String>) {
    var str = "Hello world"
    println(str div "l") //Output: Heo word
    println(str div "l" div "o") //Output: He wrd
}

**Note: * * infix function calls have lower priority than arithmetic operators, type conversion and rangeTo operators. The following expression is 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, infix function calls take precedence over Boolean operators & & and |, is - and in - detection, and some other operators. These expressions are also equivalent:

  • A & & B XOR C is equivalent to a & & (b XOR C)
  • a xor b in c is equivalent to (a xor b) in c

See its for a complete priority hierarchy Grammatical reference.

Single expression function

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

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

When the return value type can be inferred by the compiler, it is optional to explicitly declare the return type:

fun double(x: Int) = x * 2

Function parameters and return values

Variable parameters

The parameter of the function (usually the last parameter) can be marked vararg, which will treat the parameter as a variable parameter. The so-called variable parameter means that you can have as many parameters as you want, and these parameter values will be processed as an array within the function. The asList function below is a generic function with only one parameter and variable parameters. This function returns the type list < T >. The function of the asList function is to convert a set of values into a list < T > object and return the object.

fun <T> asList(vararg ts:T):List<T>{
    var result=ArrayList<T>()
  for (t in ts){
      result.add(t)
  }
    return result
}

Since ts is a variable parameter, any number of parameter values can be passed and can be of any type.

fun main(args: Array<String>) {
    var result = asList(0, "hello", 1, 2, 3, 4, 5,"world")
    println("result = [${result}]")
}

Inside the asList function, the vararg parameter is regarded as an array of type T, that is, the ts variable in the asList function is of type array < out t >.

Only one parameter can be marked vararg. If the vararg parameter is not the last parameter of the function, you can use the named parameter syntax to pass the parameter value for other parameters after the vararg parameter, or if the parameter type is a function, you can pass a lambda expression outside the parentheses. For example, the asList function below has three parameters. The first parameter is a variable parameter, and the last two are value1 and value2 parameters. Since the last parameter is not a variable parameter, you need to use named parameters when passing the values of value1 and value2 parameters. Lambda expressions are described in detail later.

fun <T> asList(vararg ts: T, value1: Int, value2: String): List<T> {
    var result = ArrayList<T>()
    for (t in ts) {
        result.add(t)
    }
    println("value1 = [${value1}], value2 = [${value2}], result = [${result}]")
    return result
}

fun main(args: Array<String>) {
    println(product)
    var result = asList(1, 2, 3, 4, 5, "world", value1 = 0, value2 = "hello")
    println("result = [${result}]") // Output: value1 = [0], value2 = [hello], result = [[1, 2, 3, 4, 5, world]]
}

When calling a function with vararg parameters, we can pass the parameter values one by one, such as asList(1,2,3). Or, if we already have an array and want to pass its contents to the function, we can use the spread operator (add a * before the array):

fun main(args: Array<String>) {
    var a = arrayOf(0, "hello", "world", 1)
    asList(-1, 3, *a, value1 = 87, value2 = "98") //Output: value1 = [87], value2 = [98], result = [[-1, 3, 0, hello, world, 1]]
}

return type

If the function body is a code segment composed of multiple lines of statements, the return value type must be explicitly specified, unless the function returns Unit (no value), and the declaration of the return type can be omitted. For a function composed of multiple lines of statements, Kotlin will not infer its return value type, because there may be a complex control flow inside such a function, and the return value type is not so clear to the code reader (sometimes, it is difficult to determine the return value type even for the compiler).

Scope of function

In Kotlin, functions can be defined at the top level of the source code, which means that you don't have to be like in Java

As in C# or Scala, create a class to hold this function. In addition to the top-level functions, the functions in Kotlin can also be defined as local functions, member functions and extension functions.

Local function

Kotlin supports local functions, that is, functions nested within another function.

fun saveFile() {
    // Local function
    fun getFullFileName(fn: String): String {
        return "/Users/$fn"
    }

    var fileName = getFullFileName("test.txt")
    println("$fileName Saved successfully") // Output: / Users/test.txt has been saved successfully
}

Local functions can access local variables in external functions. Therefore, in the above example, fn can be defined as a local variable.

fun saveFile() {
    var fn = "test.txt"

    // Local function
    fun getFullFileName(): String {
        return "/Users/$fn"
    }

    var fileName = getFullFileName()
    println("$fileName Saved successfully") // Output: / Users/test.txt has been saved successfully
}

Member function

Member functions are functions defined inside a class or object:

class Sample {
    // Member function
    fun foo() { print("Foo")}
}

Member functions are called in point notation:

Sample().foo() // Create an instance of class Sample and call foo

Generic Functions

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

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

For more information about generic functions, see generic paradigm.

Inline function

Using higher-order functions brings some loss of runtime efficiency: each function is an object and captures a closure. That is, those variables that will be accessed in the function body. Memory allocation (for function objects and classes) and virtual calls introduce runtime overhead.

However, in many cases, these runtime consumption can be eliminated by inlining Lambda expressions where they are used. For functions to support inlining, you need to use the inline keyword when defining functions.

For inline functions, you can view This article .

Keywords: Android kotlin

Added by shamuntoha on Fri, 26 Nov 2021 11:25:26 +0200