《转》Kotlin开发安卓APP笔记-函数和lambda表达式(3)

接着上一篇【Kotlin开发安卓APP笔记-函数和lambda表达式(2)】继续学习函数和lambda表达式
转自:http://blog.csdn.net/tangxl2008008/article/details/53282100
http://blog.csdn.net/tangxl2008008/article/details/53665671

高阶函数

将函数作为参数或返回一个函数,称为高阶函数。如“lock()”函数,给对象和函数提供锁功能,获取锁,执行函数,释放锁。

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

该函数的参数“body”是一个函数类型:“() -> T”,表示为一个函数,没有入参,返回一个“T”的值。
通过下面方式调用,需要传入一个函数类型参数:

fun toBeSynchronized() = sharedResource.operation()  

val result = lock(lock, ::toBeSynchronized)  

另外,也使用使用Lambda表示方式:

val result = lock(lock, { sharedResource.operation() })  

Lambda表达式详细内容见“Lambda表达式”部分,这里先简要概述:
Ø Lambda表达一般使用“{ }”包围。
Ø 它的参数(如果有的话)在“->”前定义,参数类型可能是省略的。
Ø 函数体跟在“->”后面。
在Kotlin中,若函数最后一个参数为函数类型,调用时,该参数可以放到函数“()”的外面:

lock (lock) {  
  sharedResource.operation()  
}  

另一个高阶函数例子“map()”:

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

可以这样调用,当只有Lambda表达式参数时,调用函数时后面的“()”也可以省略:

var ints = asList(1, 2, 3, 4)  
val doubledList = ints.map { it -> it * 2 }  

若函数参数对应的函数只有一个参数,在使用时,可以省略参数定义,直接使用“it”代替参数:

ints.map { it * 2 }  

这种省略参数方式可以写成语言集成查询模式(LINQ-style)代码:

strings.filter { it.length == 5 }  
.sortBy { it }  
.map { it.toUpperCase() }  

内联函数(Inline Functions)

使用高阶函数造成一些运行时问题:每一个函数都是一个对象,它会持有一个闭包;即在函数体中可以访问这些变量。内存分配(包括函数对象和类)及虚拟调用都会作为运行开销。

通过内联Lambda表达式方式,可以减少这种开销。如“lock()”函数,可以容易使用在使用位置内联相关函数,考虑下面使用方式:

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

//  
lock(l) { foo() }  

这种使用方式,会创建一个函数对象作为参数并使用它,编译器会生成下面的代码:

l.lock()  
try {  
  foo()  
}  
finally {  
  l.unlock()  
}  

内联函数,需要在函数前面使用“inline”修饰:

inline fun lock<T>(lock: Lock, body: () -> T): T {  
  // ...  
} 

使用“inline”会影响函数本身及传入的Lambda表达式参数,它们都会嵌入到它们的调用位置。

内联方式会增加生成的代码,需要合理的使用它(不要内联一个复杂功能的大函数),可以提高性能,尤其在循环中。

非内联(noinline)

有时,只需要将内联函数的部分参数使用内联Lambda,其他的参数不需要内联,可以使用“noinline”关键字修饰:

inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {  
  // ...  
}  

内联Lamdbd表达式(Inlinable lambdas)只能在内联函数中或作为内联函数的参数,而非内联表达式在所有中操作,如储存到字段,传递等。
注:若一个内联函数没有任何的内联参数,并且也没有具体化类型参数(reified type parameters),编译器会抛出一个警告,提示内联函数没有实际意义(若认为内联定义时又必要的,也可以忽略该警告)。

非局部返回(Non-local returns)

在kotlin中,使用一个默认的无限制的“return”,会返回对应的函数或匿名函数。若需要返回一个Lambda,需要使用标签;由于Lambda不能直接返回一个封闭的函数,在Lambda中不允许直接使用单独的return。

fun foo() {  
  ordinaryFunction {     //普通函数  
     return // ERROR: can not make `foo` return here  
  }  
}  

但是,若函数表达式是内联的,“return”是可以直接使用的:

fun foo() {  
  inlineFunction {  
    return // OK: the lambda is inlined  
  }  
}  

这类返回(位于Lambda中,但退出的是外层的封闭函数)称之为非局部返回(non-local returns)。如:

fun hasZeros(ints: List<Int>): Boolean {  
  ints.forEach {  
    if (it == 0) return true  // returns from hasZeros  
  }  
  return false  
}  

注:一些内联函数,不是直接在函数体中使用lambda参数,而是通过其他执行上下文,如具体对象或嵌套函数等。这种情况下,不能在Lambda中使用非局部返回。为了表明该种情况,可以在参数前使用“crossinline”关键字修饰标识。

inline fun f(crossinline body: () -> Unit) {  
    val f = object: Runnable {  
        override fun run() = body()  
    }  
    // ...  
}  

“break”和“continue”还不能再内联Lambd中使用;但在考虑支持他们。

具体化类型参数(Reified type parameters)

有时候,需要使用一个类型作为参数,如:

fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {  
    var p = parent  
    while (p != null && !clazz.isInstance(p)) {  
        p = p?.parent  
    }  
    @Suppress("UNCHECKED_CAST")  
    return p as T  
}  

如,假设爬上一颗“tree”,使用反射去检测一个节点是否是某类型。下面方式可以实现,但不是最优方式:

myTree.findParentOfType(MyTreeNodeType::class.java)  

实际上,只需要将一个类型传递给该函数;如像这种方式:

myTree.findParentOfType<MyTreeNodeType>()  

为了支持这种方式,内联函数支持具体化类型参数(reified type parameters),实现方式:

inline fun <reified T> TreeNode.findParentOfType(): T? {  
    var p = parent  
    while (p != null && p !is T) {  
        p = p?.parent  
    }  
    return p as T  
}  

类型参数使用“reified”关键字修饰,就可以在函数体中访问,其他基本跟一般的类一样。若函数为内联函数,不再需要反射方式,如“!is”,“as”操作符都可以使用。可以使用下面方式使用:

myTree.findParentOfType<MyTreeNodeType>()  

尽管反射在很多情况下不需要,但还是可以在具体化类型参数上使用它。

inline fun <reified T> membersOf() = T::class.members  

fun main(s: Array<String>) {  
  println(membersOf<StringBuilder>().joinToString("\n"))  
}  

注:普通函数(非内联函数),不能包含具体化类型参数;若一个类型没有运行时表示(run-time representation)(如非具体化类型参数(non-reified type parameter)或虚拟类型,比如“Nothing”)不能作为一个具体化类型参数的实参。

Lambda表达式和匿名函数

一个Lambda表达式或一个匿名函数 是 一个函数直接量;即函数本身是没有定义,而是通过立即当做一个函数。如下面的例子:

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

“max”是一个高阶函数,它的第二个参数需要一个函数。第二个参数值本身就是一个函数,即函数直接量;它等同于下面的函数:

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

函数类型(Function Types)

一个函数接收另外一个函数作为参数,需要指定该参数作为函数类型参数。如“max”函数:

fun <T> max(collection: Collection<T>, less: (T, T) -> Boolean): T? {  
  var max: T? = null  
  for (it in collection)  
    if (max == null || less(max, it))  
      max = it  
  return max  
}  

参数“less”的类型为“(T, T) -> Boolean”,即表示入参为两个类型为“T”的参数,返回一个“Boolean”值的函数;true表示第一个值小于第二个值。
第4行将“less”当做一个函数使用。
一个函数类型可以通过上面方式实现,若想记录每个参数的意义,也可以定义成一个变量方式:

val compare: (x: T, y: T) -> Int = ... 

Lambda表达式语法

Lambda表达式句法形式 就是 一个函数类型文本,如:

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

一个Lambda表达式通常使用“{ }”包围,参数是定义在“()”内,可以添加类型注解,实体部分跟在“->”后面;下面为一个去掉所有的可选注解的Lambda表达:

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

经常情况下面,Lambda表达式只有一个参数,可以不定义该参数,注解使用“it”关键字代替:

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

注:若函数的最后一个参数为函数参数,可以将Lambda表达式定义到参数列表的“()”外面。

匿名函数(Anonymous Functions)

前面的Lambda表示定义时,可以明确定义返回值类型;在大部分情况下,没有必要明确定义的,因为返回值类型基本都可以自动推断出。
需要明确定义返回值类型,也可以使用匿名函数(anonymous function)代替。

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

匿名函数除了省略了函数名称,其他跟一般函数的定义基本类似,函数体可以是一个表达式或其一个代码块。

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

上面的匿名函数的参数及返回类型跟一般函数一样,都是明确定义的;若参数类型可以通过上下文推断出来,也可以省略:

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

匿名函数的返回类型跟一般函数一样:对应只有一行执行代码的函数,编译器可以自动推断出来返回类型,可以省略;对应多方代码块的函数,需要显示定义返回值类型(为Unit可以省略)。
匿名函数 与 Lambda表示式区别:
Ø 匿名函数作为参数,一般定义在“()”中;而Lambda表达式可以定义到调用函数“()”外。
Ø 另外区别在“非局部返回(non-local returns)”行为上:非标签注解的return(返回对应的最内层的函数(即fun)),在匿名函数中,退出该匿名函数;而在Lambda表达中,退出包含该表达式的函数。

//在Lambda中使用return  
fun testReturn1() {  
    println("testReturn1-1")  

    var intList = asList(1, 2, 3, 4)  
    println(intList)  
    var reusltList = intList.map {  
        it * 2  
        return  
    }  
    println(reusltList)  
    println("testReturn1-2")  
}  
//输出结果,Lambda中的return返回对对应的被包含的函数(即testReturn1()):  
testReturn1-1  
[1, 2, 3, 4]  

//在匿名函数中使用return  
fun testReturn2() {  
    println("testReturn2-1")  

    var intList = asList(1, 2, 3, 4)  
    println(intList)  
    var reusltList = intList.map(fun(item: Int): Int {  
        return item * 2  
    })  
    println(reusltList)  
    println("testReturn2-2")  
}  
//输出结果:  
testReturn2-1  
[1, 2, 3, 4]  
[2, 4, 6, 8]  
testReturn2-2  

闭包(Closures)

Lambda表达式及匿名函数(以及局部函数,对象表达式)可以访问包含它的外部范围定义的变量(Java中只能是常量,在Kotlin中可以是变量):

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

函数文本接收器(Function Literals with Receiver)

Kotlin提供一种特殊的接收者对象( receiver object),可以访问函数文本。在函数文本的内部,可以访问接收者的成员;类似于扩展函数,在函数体中访问接收者的成员。
对接收者,函数文本相当于一个函数类型:

sum : Int.(other: Int) -> Int  
//val sum : Int.(other: Int) -> Int = { this + it }  

就可以当做一个函数调用:

1.sum(2)  

可以使用匿名函数方式:

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

可以使用Lambda表达式方式实现:

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  
}  

除了手动合并了两篇,并未做任何修改,侵删

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值