Kotlin 函数

213 阅读4分钟

Kotlin 函数

函数声明

1.覆盖方法总是使用与基类型方法相同的默认参数值。 当覆盖一个带有默认参数值的方法时,必须从签名中省略默认参数值

2.如果一个函数不返回任何有用的值,它的返回类型是 Unit

// 使用 fun
fun xxx(x: Int): Int {
    return x * x
}
open class A {
    
    //函数参数可以添加默认值
    open fun show(a: Int = 0 , b: Int = 1) {
        println( a + b)
    }
}

class AA: A() {

    override fun show(a: Int, b: Int) {
    }

    //单表达式函数
    fun  show1() : Int = 2
    //编译器推断返回值类型
    fun  show2() = 2

}

fun main() {
    val a = A()
    a.show(b = 2) // 可以省略部分默认有默认值的参数
}

可变数量的参数(Varargs)

1.函数的参数(通常是最后一个)可以用 vararg 修饰符标记

2.在函数内部,类型 Tvararg 参数的可见方式是作为 T 数组,即上例中的 ts 变量具有类型 Array <out T>

fun <T> asList(vararg ts: T): List<T> {
    val result = ArrayList<T>()
    for (t in ts) // ts is an Array
        result.add(t)
    return result
}
//调用
val list = asList(1, 2, 3)

中缀表示法

1.标有 infix 关键字的函数也可以使用中缀表示法(忽略该调用的点与圆括号)调用。中缀函数必须满足以下要求:

2.中缀函数总是要求指定接收者与参数。当使用中缀表示法在当前接收者上调用方法时,需要显式使用 this

infix fun Int.shl(x: Int): Int { …… }

// 用中缀表示法调用该函数
1 shl 2

// 等同于这样
1.shl(2)

// this 使用
class MyStringCollection {
    infix fun add(s: String) { …… }
    
    fun build() {
        this add "abc"   // 正确
        add("abc")       // 正确
        add "abc"        // 错误:必须指定接收者
    }
}

函数作用域

1.Kotlin 中函数可以在文件顶层声明

2.Kotlin 中函数也可以声明在局部作用域、作为成员函数以及扩展函数

// 函数内定义函数 嵌套函数
fun dfs(graph: Graph) {
    fun dfs(current: Vertex, visited: Set<Vertex>) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v, visited)
    }

    dfs(graph.vertices[0], HashSet())
}

泛型函数

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

fun <T> singletonList(item: T): List<T> { …… }

内联函数

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

2.inline 修饰符影响函数本身和传给它的 lambda 表达式

3.内联可能导致生成的代码增加;不过如果我们使用得当(即避免内联过大函数),性能上会有所提升,尤其是在循环中的“超多态(megamorphic)”调用处

高阶函数

1.高阶函数是将函数用作参数或返回值的函数

2.使用高阶函数会带来一些运行时的效率损失:每一个函数都是一个对象,并且会捕获一个闭包。 即那些在函数体内会访问到的变量。 内存分配(对于函数对象和类)和虚拟调用会引入运行时间开销

3.it是在当一个高阶函数中Lambda表达式的参数只有一个的时候可以使用it来使用此参数。it可表示为单个参数的隐式名称,是Kotlin语言约定的

fun show1(a: (String) -> String): String {
    return  a("0")
}
fun main() {
    println(show1 { it + "22" })
}

Lambda 表达式

1.lambda表达式就是一个匿名函数

2.在lambda中不可用直接使用return ,可以使用return+label这种形式

3.在使用Lambda表达式的时候,可以用下划线(_)表示未使用的参数,表示不处理这个参数。

// 按钮点击事件
mBtn.setOnClickListener(object : View.OnClickListener{
        override fun onClick(v: View?) {
            Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show()
        }
 })
 //等同于
mBtn.setOnClickListener { Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show() }
 
val map = mapOf("key1" to "value1","key2" to "value2","key3" to "value3")

map.forEach{
     key , value -> println("$key \t $value")
}

// 不需要key的时候
map.forEach{
    _ , value -> println("$value")
}
 

尾递归函数

1.Kotlin 支持一种称为[尾递归]的函数式编程风格。 这允许一些通常用循环写的算法改用递归函数来写,而无堆栈溢出的风险

2.当一个函数用 tailrec 修饰符标记并满足所需的形式时,编译器会优化该递归,留下一个快速而高效的基于循环的版本

3.要符合 tailrec 修饰符的条件的话,函数必须将其自身调用作为它执行的最后一个操作。在递归调用后有更多代码时,不能使用尾递归,并且不能用在 try/catch/finally 块中。目前尾部递归只在 JVM 后端中支持

// 使用
val eps = 1E-10 // "good enough", could be 10^-15

tailrec fun findFixPoint(x: Double = 1.0): Double
        = if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))
        
// 等于

val eps = 1E-10 // "good enough", could be 10^-15

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