函数式编程
函数式编程(FP,Functional Programming),又称为泛函编程,是一种编程范式:一切皆是函数。函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数传入另一个函数,或者作为别的函数的返回值。
fun main(args: Array<String>) {
val print = fun(x: Any) { print(" $x") }
listOf(1, 2, 3).forEach(print) // 1 2 3
}
闭包就是把函数以及变量包起来,使得变量的生存周期延长。
闭包的组成:
1. 要执行的代码块{}。
2. 自由变量的左右域。
函数声明
/**
* (Int)->Int 是函数类型声明,表示一个从Int映射到Int的函数。
*/
fun triple(double: (Int) -> Int): (Int) -> Int {
return { x -> double(x) + x }
}
fun main(args: Array<String>) {
val currentDouble = fun(x: Int): Int = x + x
println(triple(currentDouble)(4)) // 12
}
扩展函数
通过“扩展”声明完成一个类的新功能扩展,而无需继承该类或使用设计模式(如装饰模式)。
/**
* 拨打电话,扩展函数内部对应接收者对象(传过来的点符号的对象)
*/
fun Activity.toCall(mobile: String?) {
if (TextUtils.isEmpty(mobile)) {
return
}
val intent = Intent(Intent.ACTION_DIAL, Uri.parse("tel:$mobile"))
startActivity(intent)
}
函数参数
默认参数
函数参数可以有默认值,当省略相应的参数时使用默认值,减少函数重载。重写的方法(子类中)不能有默认值。
fun add(x: Int, y: Int = 0): Int {
return x + y
}
fun main(args: Array<String>) {
println(add(2)) //2
println(add(2, 3)) //5
}
命名参数
参数有多个时候,不好理解,可以加上命名参数。
// ()中的startIndex 表示此参数的定义名称
val substring = name?.substring(startIndex = 1)
可变参数
参数的参数(通常是最后一个)可以用vararg修饰符标识。
fun main(args: Array<String>) {
println(asList(2, 3, 4)) // [2, 3, 4]
}
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
result += ts // for(t in ts){result.add(t)}
return result
}
函数作用域
局部函数(嵌套函数)指一个函数在另一个函数内部。
fun add(x: Int = 0, y: Int = 0): Int {
fun addMore(x: Int): Int {
return 10 + x
}
return addMore(x) + y
}
fun main(args: Array<String>) {
val add = add()
println(add) // 10
}
成员函数是在类或者对象内部定义的函数。
高阶函数
高阶函数是将函数用作参数或返回值得函数。
函数类型声明的语法是:(x)->Y
fun isOdd(x: Int): Boolean {
return x % 2 == 1
}
// :: 用来引用一个函数,省略了(this::isOdd)表示把一个方法当做一个参数,传递到另一个方法中进行使用
fun main(args: Array<String>) {
val list = listOf(1, 2, 3)
list.filter(::isOdd)
.let { print(it) } // [1, 3]
}
匿名函数
list.filter(fun(x: Int): Boolean {
return x % 2 == 1
}).let { println(it) } // [1, 3]
Lambda表达式
list.filter({ it % 2 == 1 })
- lambda表达式总是被大括号{}括着。
- 其参数(若有)在->之前声明(参数类型可以省略)
函数体(若有)在->之后
- 若入参lambda表达式是唯一函数,那么调用中圆括号可以省略。
list.filter({ it % 2 == 1 })
// 等价于
list.filter{ it % 2 == 1 }
Java中参数可以加括号,也可以省略括号,但是不能使用解构声明语法中的下划线(_)
txt.setOnClickListener((v)->{// do Something});
// 也可以使用:v->{}
Kotlin中参数加括号报错,解构声明语法。
// txt.setOnClickListener((v)->{ }) // error
txt.setOnClickListener({ v -> /*do something*/ }) // 可以
// 以下就可以加括号
val map = emptyMap<String, String>()
map.mapValues { (key, value) -> "$value!" }
map.mapValues { (entry) -> "$entry!" }
- it:单个参数的隐式名称
约定:如果函数字面值只有一个参数,那么它的声明(连同->)都可以省略。
list.map { it * 2 }
- 带接收者的函数字面值
class HTML{
fun body(){
println("HTML BODY")
}
}
fun html(data:HTML.()->Unit):HTML{ // HTML.() 中的HTML是接收者类型
val html = HTML() // 创建接收者对象
html.data() // 将该接收者对象传给改lambda
return html
}
// 带接收者的函数字面值
fun main(args: Array<String>) {
val sum = fun Int.(other: Int): Int = this + other
println(1.sum(2)) // 3
html { body() } // HTML BODY
}
内联函数
用inline修饰的函数,内联函数支持“具体化的类型参数”。参考Kotlin基础之内联函数
使用高阶函数会给运行时带来一些坏处:每个函数都是一个对象,捕获闭包(如:访问函数体内的变量),内存分配(函数对象或Class),虚拟调用引入的运行过载。 使用内联Lambda表达式在多数情况下可以消除这种过载,即通过编译预处理,省去函数调用的开销,提供运行效率,但是会以代码膨胀为代价。
inline fun <reified T: Activity> Activity.newIntent() {
val intent = Intent(this, T::class.java)
startActivity(intent)
}
尾递归
当一个函数用tailrec修饰符标记并满足所需的形式时,编译器会优化该递归,生成一个快速而高效的基于循环的版本。(优化后无堆栈溢出风险)
要符合tailrec修饰符的条件,函数必须将其自身调用作为他执行的最后一个操作。在递归调用后有更多代码时,不能使用尾递归,并且不能用在try/catch/finlly块中。
fun main(args: Array<String>) {
println(findFixPoint(2.0)) //0.7390851332151607
}
tailrec fun findFixPoint(x: Double = 1.0): Double = if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))