Lambda作为形参和返回值
声明高阶函数
- 任何以lambda或者函数引用作为参数的函数,或者返回值,或者两者都有,就是高阶函数。比如list.filter
(4,"abc")-> {}
如下:
{ x, y -> x + y}
这里省略了参数x,y类型,因为在其他部分已经制定了,不需要在lambda函数体再次声明
val funOrNull: ((Int, Int) -> Int)? = null
复制代码
- 可以为函数参数制定名字
//声明
fun performRequest(url: String,callback: (code: Int, content: String) -> Unit
){}
//调用
val url = "http://baidu.com"
performRequest(url,{code, content -> /*code*/ })
performRequest(url){code, content -> /*code*/ }//根据kotlin约定,lambda为最后一个参数,可以放到括号外面
复制代码
- 调用函数
//声明一个函数,参数为一个函数,这个函数有2个参数都是int值,返回值也为int
fun towAndThree(operator: (x: Int, y: Int) -> Int) {//这里的x,y是可以省略的,省略了,IDE代码补全会不方便val result = operator(3, 4)print("the result is $result")
}
复制代码
- filter()函数
//声明
public inline fun <C : Appendable> CharSequence.filterTo(destination: C, predicate: (Char) -> Boolean): C {for (index in 0 until length) {val element = get(index)if (predicate(element)) destination.append(element)}
return destination
}
//调用
println(url.filter { it in 'a'..'z' })
复制代码
- 在Java中使用函数类 原理:函数类型被声明为普通的接口:一个函数类型的变量,是
FunctionN
接口的一个实现。Kotlin
标准库定义了一系列的接口比如Function0<R>
(没有参数的函数),Function1(P1,R)
(一个参数的函数),调用这个方法就会执行函数。一个函数类型的变量就实现了对应的FunctionN
接口的实现类的实例,实现类invoke
方法包含了lambda
函数体
//kotlin声明
fun processTheAnser(f: (Int) -> Int){}
//java 调用 java 8
processTheAnder(num->num+1)
//在Java8以下的版本这样调用
procressTheAnder{new Function1<Integer,Integer>(){@Overridepublic Integer invoke(Integer number){return number + 1}}
}
复制代码
函数类型的参数默认值和null
值
fun <T> Collection<T>.joinToString(separator: String = ", ",prefix: String = "",postfix: String = ""
): String {val result = StringBuilder(prefix)for ((index, value) in this.withIndex()) {if (index > 0) result.append(separator)result.append(value.toString())}result.append(postfix)return result.toString()
}
复制代码
返回函数的函数
- 比如运输费用依赖于运输方式,可以定义一个函数,用来选择恰当的逻辑变体,并将它作为一个函数返回
fun getShoppingCarCacul(delivery: Delivery): (Order) -> Double {if (delivery == Delivery.STANDARD) {return { order: Order -> 6 + 2.1 * order.itemCont }}return { order -> 1.2 * order.itemCont }
}
//调用
var shoppingCarCacul = getShoppingCarCacul(Delivery.EXPEDITED)
println("产生的运费:${shoppingCarCacul(Order(4))}")
复制代码
- eg:通过输入开头文字,过滤联系人列表
data class Man(val firstName: String,val lastName: String,val phoneNumber: String?
)class ContactListFilter() {val prefix = ""val isOnlyPhoneNumber = falsefun qetPredicate(): (Man) -> Boolean {val startWithPrefix = { man: Man -> man.firstName.startsWith(prefix) || man.lastName.startsWith(prefix) }//这里不能使用it,因为IDE推断不出it代表是什么if (!isOnlyPhoneNumber) {return startWithPrefix}return { man: Man -> startWithPrefix(man) && man.phoneNumber != null }//这里可以是用it,这里是根据方法的返回值类型,推断出it代码得man}
}
//调用val mans = listOf(Man("Bob", "jack"), Man("BooBo", "jack", "13800138000"), Man("kk", "jack", "13800138000"))val contactListFilter = ContactListFilter()with(contactListFilter) {prefix = "B"isOnlyPhoneNumber = false}
println(mans.filter(contactListFilter.qetPredicate()))
复制代码
使用lambda去除重复代码
val log = listOf(SiteVisit("/", 34.0, OS.WINDOWS),SiteVisit("/", 22.0, OS.MAC),SiteVisit("/", 12.0, OS.WINDOWS),SiteVisit("/sign_up", 8.0, OS.IOS),SiteVisit("/", 16.3, OS.ANDROID))
fun List<SiteVisit>.averageTime(os: OS) = filter { it.os == os }.map { it.duration }.average()//调用
println(log.averageTime(OS.WINDOWS))
复制代码
- 如果说要查询WINDOW、和Android的呢?后面又需要改成来自iOS的注册页面停留时间是多少呢?这个时候lambda就派上用场了。
fun List<SiteVisit>.averageTime(predicate: (SiteVisit) -> Boolean) = filter(predicate)//这里的筛选改为lambda。.map { it.duration }.average()
复制代码
把过滤条件转换成lambda表达式,实现去除重复代码
策略模式(你需要声明一个接口,并且为每一种可能实现不同的策略)可以通过lambda简化
内联函数:消除lambda运行时带来的开销
在kotlin中,每创建一个lambda
表达式就会创建一个匿名类,so,每次调用都会创建一个对象,会带来额外的开销这个时候inline
就出现了。
一个被inline修饰的函数,函数体会直接替换到函数被调用的地方,而不是正常调用。一般来说,参数如果被直接调用或者作为参数传递给另外一个inline
函数。他是可以被内联的。
- 内联函数使用限制-》不能把内联函数保存到一个属性?
内联集合操作
Kotlin
标准库中,集合函数,比如filter
,map
等函数已经inline函数,不会产生额外的对象。在处理比较大的集合,应该使用序列asSequence
进行操作。
怎样决定是否使用lambda
- 使用inline关键字只能提高带有lambda参数的函数的性能。
- 对于普通函数JVM已经提供了强大的内联支持。
- 应该保证inline修饰的函数比较小。-》因为需要把函数字节码拷贝到每一个调用点上。
使用lambda管理资源
通常在资源管理中,需要在try
里获取资源,在finally
中释放资源可以把这一部分封装成lambda
表达式 在Kotlin中,加锁使用withLock
函数 :try-with-resource 语句
static String readFirstLineFromFile(String path) throws IOException {try (BufferedReader br = new BufferedReader(new FileReader(path))) {return br.readLine();}//这里不需要写关闭资源的语句,
}
复制代码
在Kotlin中使用use
实现相同的结果,用于操作可关闭的资源。
fun readFirstLineFromFile(path: String): String = BufferedReader(FileReader(path)).use { br -> br.readLine() }
复制代码
高阶函数中的控制流
- 使用return 从一个封闭的函数中返回。
val list = listOf(Person("Bob", 18, 20000), Person("Jack", 19, 20000))list.forEach {if (it.name=="Bob") {println("Found!")return //直接返回方法}}
复制代码
- 从lambda返回
val list = listOf(Person("Bob", 18, 20000), Person("Jack", 19, 20000))list.forEach {if (it.name=="Bob") {println("Found!")return@forEach //直接返回方法}}
复制代码
同样的规则也适用于this表达式
- 匿名函数:默认使用局局部返回
list.forEach {if (it.name=="Bob") returnprintln("Found!")}
复制代码
在filter
中使用匿名函数
list.filter(fun(person): Boolean {return person.age > 10})
复制代码
如果是使用表达式函数体,可以省略返回值类型,可以简化为
list.filter(fun(person) = person.age > 10
)
复制代码
End