Kotlin Reference (十五) 函数和lambda表达式:普通函数和高阶函数

来源:互联网 发布:淘宝镁光内存条 编辑:程序博客网 时间:2024/05/17 07:28

KotLin 相关文档


官方在线Reference
kotlin-docs.pdf
Kotlin for android Developers 中文翻译
Kotlin开发工具集成,相关平台支持指南
Kotlin开源项目与Libraries
Kotlin开源项目、资源、书籍及课程搜索平台
Google’s sample projects written in Kotlin
Kotlin and Android

 

函数


Kotlin中的函数,必须以fun关键字来定义;函数参数,必须声明类型,以:来分隔
如下,test函数,参数为一个 名为a的Int型变量:

fun test(a: Int) {}

使用infix关键字

使用infix关键字,特征:

  • 可用在扩展函数、成员函数前

  • 参数只有一个

在后续调用时, 可以形如:x <funName> y

如对Int,扩展一个名为shll的函数:

infix fun Int.shll(x: Int): Int {    return this shl x}

调用:

1 shll 2 //<==> 1.shll(2)

 

含默认值的参数列表

参数必须显示声明类型

含默认值的参数列表,调用时可以省略 传入有默认值的参数;以减少函数重载数量
(当然,如果三个以上参数,中间有参数有默认值,后面参数没有,调用时是不能省略传参的)

如,

fun read(a: Int, b: Int = 0, c: Int = 0) {    }

调用如下,

read(1)       //省略b、cread(1, 2)    //省略cread(1, 2, 3) 

子类重写

基类 open一个含默认值参数列表的函数,子类要override:子类重写函数不能有默认值参数

如,

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

 

命名参数

命名参数(named arguments),即调用时,带上参数名,格式:参数名=参数值

如,

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

调用如下,

reformat("") //省略默认参数reformat(str = "",        normalizeCase = true,        upperCaseFirstLetter = true,        divideByCamelHumps = false,        wordSeparator = '_')reformat(str = "", wordSeparator = '_')

注意:如果前一个参数是命名参数,后一个就不能是非命名的;命名参数的调用名称,与对应参数的声明时名称需要一致

优点:增加可读性;有多个带默认值的参数时,还可以在调用时,跳过中间的参数

 

返回Unit

返回值为Unit:相当于返回一个空对象;可以在函数声明中,省略返回值定义;也可以在函数实现中,省略返回值语句

如,

fun printHello(name: String?): Unit {// <==> fun printHello(name: String?)    if (name != null) {        return Unit    } else {        //省略返回语句    }}

 

单个函数表达式

当函数仅返回一个表达式,可以省略函数主体(函数块)声明,形如 fun a(p: Type) = b

如,

fun doubleMultiply(x: Int): Int = x * 2 // <==> fun double(x: Int) = x * 2

 

可变参数

可变参数(Variable number of arguments),java中使用...,kotlin中使用 vararg关键字

与java类似,kotlin中的可变参数对象,也是一个数组,即 Array类型

如,

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

调用,

asList(1, 2, 3)

声明在其它参数前

可变参数只能有一个,如果声明在其它参数前:系统无法自动推断,可以使用命名参数语法进行调用。

将一个可变参数,传递给另一个函数中的可变参数,要在可变参数前使用*符号

如,再有一个函数asList2:

fun <T> asList2(vararg ts: T, a: Int): List<T> {    return asList(*ts)}

调用,

asList2(ts = arrayOf(1, 2, 3), a = 4)

参数列表中有函数类型的参数

  • 函数型参数,在可变参数前

如,

fun <T> asList3(op: () -> Unit, vararg ts: T): List<T> {    return asList(*ts)}

调用,

asList3({}, 1, 2)

注:调用时,书写不好看,不推荐;推荐后一种声明形式

  • 函数型参数,在可变参数后

如,

fun <T> asList4(vararg ts: T, op: () -> Unit): List<T> {    return asList(*ts)}

调用,

asList4(1, 2, op = {})asList4(1, 2) {}

注:调用时,函数类型写在参数内,需要使用命名参数语法;
或直接写到参数外(推荐这样的调用形式)

 

函数范围

函数可以声明在一个顶级的文件中,即kotlin-file中;

可以声明成一个类的成员函数、类的扩展函数;

可声明成一个局部函数,即函数中声明的函数;局部函数可以访问外部函数中的局部变量

 

尾递归函数

满足尾递归形式的函数(不一定要是函数表达式),使用 tailrec关键字,

编译器中会优化出一个更有效率的循环语句替代,防止栈溢出。

注意:在 try/catch/finally 语句块中,进行尾递归调用,tailrec 无法优化

如,

tailrec fun findFixPoint(x: Double = 1.0): Double     = if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))

上面的递归,相当于如下循环:

private fun findFixPoint(): Double {    var x = 1.0    while (true) {        val y = Math.cos(x)        if (x == y) return y        x = y    }}

 

高阶函数和lambda表达式


一个函数声明中,将另一个函数作为参数或返回值,这样的函数就是高阶函数(Higher-Order Functions)。

如,

fun <T> lock(lock: Lock, body: () -> T): T {     lock.lock()    try {        return body()    } finally {        lock.unlock()     }}

如上,函数参数body的类型是一个函数,书写使用 lambda 表达式:() -> T,表明这个函数中没有参数,返回类型T。在try块中直接调用body函数:body()

调用,如前文可变参数中所述:可以在参数列表中使用命名参数语法,或在参数列表外加函数体

lock(ReentrantLock(), body = {})lock(ReentrantLock()) {}

 

使用函数(方法)引用

在传递函数参数时,可以传递另一个函数签名相同的函数,这时就可以使用函数引用

如,

fun <T> usFun(a: Int, body: (arg: Int) -> T): T {//body中,也可声明成 (Int) -> T    return body(a)}fun suffix(arg: Int): String {    return "${arg}_suffix"}

上面suffix函数签名,可以用于代入到usFun中的body参数上。

调用,

val result =  usFun(3, body = { arg -> suffix(arg) })//命名参数val resultX =  usFun(4) {arg -> suffix(arg)} //一般lambdaval resultXX = usFun(5, ::suffix) //lambda中的方法引用

 

_Collections.kt中的map、forEach函数

_Collections.kt中的map函数,可以看成类似如下实现:

fun <T, R> List<T>.mapz(transform: (T) -> R): List<R> {    val result = arrayListOf<R>()    for (item in this)        result.add(transform(item))    return result}

调用

listOf(1, 2).mapz { i -> i * 2f }.forEach { println(it) }

这里只是为了演示,一般就直接调用 map函数即可

当函数型参数,其内部只有一个参数时,可使用it来指代该参数,而不使用声明成: p -> … 这样的形式

 

函数型参数内有不使用参数

函数型参数,它的其中有不使用的参数,可以用_声明

mapOf(Pair("a", 1), Pair("b", 2)).forEach { t, u -> println("t=$t, u=$u") } //两个参数,都被使用了mapOf(Pair("a", 1), Pair("b", 2)).forEach { _, value -> println("$value!") }mapOf(Pair("a", 1), Pair("b", 2)).forEach { _, _ -> println("3!") }

 

属性为函数型

//val vv = println("aa") //声明一个 类型为函数形的属性val vv: (Any?) -> Unit = ::println //声明一个 类型为函数形的属性fun aa() = vvaa()

上面两种声明 vv的方式,是等价的。
 

匿名函数代替函数型参数

listOf(3, 4).filter { it > 0 }listOf(3, 4).filter(fun(item) = item > 0) //fun(item)为匿名函数

 

字面函数和接收者

字面函数和接收者(Function Literals with Receiver)

示例不好理解,如下:

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

sum实际上是一个函数,调用:

1.sum(3)

再例:

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}

body()属性Html的,内部使用了html.init()接收一个函数: () -> Unit,所以这里省略了Html(),即没有用Html().body() 这样的语句来调用

 

原创粉丝点击