Kotlin 函数

177 阅读5分钟

函数声明

Kotlin中的函数使用fun关键字声明,函数参数使用Pascal表示法定义,即attribute: Type。每个参数必须有显示类型。

单表达式函数:当函数返回单个表达式时,可以省略花括号并且在=符号之后指定代码体即可。注意:Kotlin不推断具有代码块体的函数的返回类型,所以若具有代码块体的函数只有在返回Unit类型时,可以省略返回值类型。以下截图展示这一区别👇

上图中,若不显示著名函数返回类型为Int,就会报错。

(如果一个函数不返回任何有用的值,他的返回类型是Unit。所以return Unitreturn等同,并且是可选的)

* 可以使用类型别名给函数类型起一个别称:

参数

Kotlin中,函数参数可以有默认值,当省略相应的参数时使用默认值。**与其他语言相比,这可以减少重载数量。**如果一个默认参数在一个无默认值的参数之前,那么该默认值只能通过使用具名参数调用该函数来使用。

覆盖方法总是使用与基类型方法相同的默认参数,且必须从签名中省略默认参数值。

可变数量参数:参数前用vararg修饰,此时参数被当作数组来处理。

中缀表示法

标有infix关键字的函数也可以使用中缀表示法调用。中缀函数必须满足以下要求:

1. 它们必须是成员函数或扩展函数;

2. 它们必须只有一个参数;

3. 其参数不得接受可变数量的参数且不能有默认值。 

中缀函数举例:until, xor, union, ... 🌰 :0 until 10 , xs union ys , true xor true

还有to:val map = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 3)

这里to声明了map中key与value的关系,比java中的写法简洁了很多,Kotlin标准库中对to函数的生命如下:

public infix fun <A,B> A.to(that: B): Pair<A, B> = Pair(this, that)

局部函数

一个函数在另一个函数内部,局部函数可以访问外部函数(即闭包)的局部变量,🌰 :

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]) 
}

成员函数

成员函数是在类或对象内部定义的函数,🌰 :

class Sample{
    fun foo(){ print("Foo")}
}
//调用:
Sample().foo() // 创建类Sample实例并调用foo

泛型函数

函数可以有泛型参数,通过在函数名前使用尖括号指定,🌰 :

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

高阶函数和Lambda表达式

Kotlin语言天然支持了部分函数式特性。函数式语言一个典型的特性在于函数是头等公民--我们不仅可以像
类一样在顶层直接定义一个函数,也可以在一个函数内部定义一个函数。在上面的局部函数中有例子。
此外,我们还可以直接将函数像普通变量一样传递给另一个函数,或者在其他函数内被返回。

高阶函数是将函数用作参数或返回值的函数。例如函数式风格的fold。

lambda表达式是函数字面值,即未声明的函数,但立即作为表达式传递。lambda表达式总是括在花括号中,完整语法形式的参数声明放在花括号内,函数体跟在一个->符号之后,如果推断出的该lambda的返回类型不是Unit,那么该lambda主体中的最后一个表达式会视为返回值。🌰

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

其中max为高阶函数,它的第二个参数是一个lambda表达式。

在Kotlin中有一个约定:如果函数的最后一个参数是函数,那么作为响应参数传入的lambda表达式可以放在圆括号之外。下图🌰 中两个函数是一样的,第一个就把lambda函数放在圆括号之外了👇

另外,一个lambda表达式只有一个参数是很常见的。如果编译器自己可以识别出签名,也可以不用声明唯一的参数并忽略->。该参数会隐式声明为it 🌰 :

ints.filter { it > 0 } // 这个字面值是 (it: Int) -> Boolean 类型的

内联函数

有时使用内联函数可以为高阶函数提供灵活的控制流。

内联函数是C++的增强特性之一,用来降低程序的运行时间。当内联函数收到编译器的指示时,即可发生内联:编译器将使用函数的定义体来替代函数调用语句,这种替代行为发生在编译阶段而非程序运行阶段

🌰 :

例子来源:白话kotlin:内联函数助你提升运行效率尾递归函数

概念补充

静态类型语言(Statically Typed Language):

编译器间做检查数据类型的语言,即写程序时要声明所有变量的数据类型,是固定的。使用数据之前,必须声明数据类型(int, float, double等)。相当于使用之前,首先要为它们分配好内存空间。

例如:C/C++是静态类型语言的典型代表,其他的静态类型语言有Kotlin, Java, C#等

优点:结构非常规范,便于调试,方便类型安全

缺点:为此需要写更多类型相关的代码,不便于阅读,不清晰明了

动态类型语言(Dynamically Typed Language):

运行期间才做数据类型检查的语言,即动态类型语言编程时,永远不用给任何变量指定数据类型。该语言会在第一次赋值给变量时,在内部将数据类型记录下来。

例如:JavaScript, Ruby, Python, php

以上概念参考编程语言傻傻分不清:弱类型、强类型、动态类型、静态类型

函数字面值(量)

函数字面值(量) 即一段函数文本,说白了就是一段代码,可以当作参数来传递。 在Kotlin 中, Lambda 表达式 、 匿名函数 ,都是一种函数字面值。英文是function literal.

在Kotlin中,lambda函数,和匿名函数属于函数字面值。

Reference

编程语言傻傻分不清:弱类型、强类型、动态类型、静态类型

白话kotlin:内联函数助你提升运行效率尾递归函数