Kotlin学习——函数与Lambda表达式、闭包

函数:
1、函数申明

Kotlin 中的函数使用 fun 关键字声明。如:

fun double(x: Int): Int {
    return 2 * x
}

2、函数默认值
●函数的参数可以有默认值

fun read(b: Array, off: Int = 0, len: Int = b.size) {
	 …… 
}

默认值通过类型后面的 = 给出的值来定义。
●重写的方法,与基类的方法有相同的默认值,重写时,不能再次定义默认值。如:

open class A {
    open fun foo(i: Int = 10) { …… }
}

class B : A() {
    override fun foo(i: Int) { …… }  // 不能有默认值
}

●当一个函数有多个默认参数时,我们可以通过命名参数来调用该函数。如:

fun foo(bar: Int = 0, baz: Int) { …… }
foo(baz = 1) // 使用默认值 bar = 0
函数:
fun reformat(str: String,
             normalizeCase: Boolean = true,
             upperCaseFirstLetter: Boolean = true,
             divideByCamelHumps: Boolean = false,
             wordSeparator: Char = ' ') {
……
}
// 使用命名参数调用:
reformat(str,
    normalizeCase = true,
    upperCaseFirstLetter = true,
    divideByCamelHumps = false,
    wordSeparator = '_'
)

●当一个函数调用混用位置参数与命名参数时,所有位置参数都要放在第一个命名参数之前。例如
允许调用 f(1, y = 2)
但不允许 f(x = 1, 2)
●可以通过使用星号操作符将可变数量参数(vararg) 以命名形式传入:

fun foo(vararg strings: String) { …… }
foo(strings = *arrayOf("a", "b", "c"))

注意,在调用 Java 函数时不能使用命名参数语法,因为 Java 字节码并不总是保留函数参数的名称
3、函数返回值
如果一个函数不返回任何有用的值,它的返回类型是 Unit。Unit 是一种只有一个值——Unit 的类型。这个值不需要显式返回。与java中的void类似。
如:

fun printHello(name: String?): Unit {
    if (name != null)
        println("Hello ${name}")
    else
        println("Hi there!")
    // `return Unit` 或者 `return` 是可选的
}
上面函数等同于
fun printHello(name: String?) { …… }

4、单函数表达式
当函数返回单个表达式时,可以省略花括号并且在 = 符号之后指定代码体即可

fun double(x: Int): Int = x * 2

该函数的返回值类型可由编译器推断,显式声明返回的类型是可选的。可写成

fun double(x: Int) = x * 2

5、可变数量的参数:
●函数的参数(通常是最后一个)可以用 vararg 修饰符标记

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

●在函数内部,类型 T 的 vararg 参数的可见方式是作为 T 数组,即上例中的 ts 变量具有类型 Array < out T>。
●只有一个参数可以标注为 vararg。如果 vararg 参数不是列表中的最后一个参数, 可以使用命名参数语法传递其后的参数的值,或者,如果参数具有函数类型,则通过在括号外部传一个 lambda。
●当我们调用 vararg-函数时,我们可以一个接一个地传参,例如 asList(1, 2, 3),或者,如果我们已经有一个数组并希望将其内容传给该函数,我们使用伸展(spread)操作符(在数组前面加 *)如:

val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)

6、中缀表示法
标有 infix 关键字的函数也可以使用中缀表示法(忽略该调用的点与圆括号)调用。中缀函数必须满足以下要求:
▲它们必须是成员函数或扩展函数;
▲它们必须只有一个参数;
▲其参数不得接受可变数量的参数且不能有默认值
如:

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"        // 错误:必须指定接收者
    }
}

7、函数作用域
在 Kotlin 中函数可以在文件顶层声明,这意味着你不需要像一些语言如 Java、C# 或 Scala 那样需要创建一个类来保存一个函数。此外除了顶层函数,Kotlin 中函数也可以声明在局部作用域、作为成员函数以及扩展函数
●局部函数
一个函数在另一个函数内部

fun dfs(graph: Graph) {
    fun dfs(current: Vertex, visited: Set) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v, visited)
    }

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

局部函数可以访问外部函数(即闭包)的局部变量,所以在上例中,visited 可以是局部变量

fun dfs(graph: Graph) {
    val visited = HashSet()
    fun dfs(current: Vertex) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v)
    }

    dfs(graph.vertices[0])
}

●成员函数
成员函数是在类或对象内部定义的函数

class Sample() {
    fun foo() { print("Foo") }
}

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

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

Lambda表达式
在java8.0以后开始支持Lambda表达式的写法。Kotlin中的Lambda与java中的Lambda相同。
语法三种形式:

  1. 无参数的情况 :
    val/var 变量名 = { 操作的代码 }

  2. 有参数的情况
    val/var 变量名 : (参数的类型,参数类型,…) -> 返回值类型 = {
    参数1,参数2,… -> 操作参数的代码
    }
    可等价于
    // 此种写法:即表达式的返回值类型会根据操作的代码自推导出来。
    val/var 变量名 = { 参数1 : 类型,参数2 : 类型, … -> 操作参数的代码 }

  3. lambda表达式作为函数中的参数的时候,这里举一个例子:
    fun test(a : Int, 参数名 : (参数1 : 类型,参数2 : 类型, … ) -> 表达式返回类型){

    }
    举例说明:

// 无参的时候
// 常规代码 
fun test(){
	println("无参数") 
} 
// lambda代码 
val test = {
	println("无参数") 
}

有参的时候
// 常规代码 
fun test(a : Int , b : Int) : Int{ 
	return a + b 
}

// lambda 
val test : (Int , Int) -> Int = {
	a , b -> a + b
}

 // 或者 
val test = {
	a : Int , b : Int -> a + b
}

// Lambda表达式作为函数中的参数的时候
// 常规代码 
fun test(a : Int , b : Int) : Int{
	return a + b 
}

fun sum(num1 : Int , num2 : Int) : Int{ 
	return num1 + num2 
}
 // 调用
test(10,sum(3,5)) // 结果为:18 

// lambda 
fun test(a : Int , b : (num1 : Int , num2 : Int) -> Int) : Int{ 
	return a + b.invoke(3,5) 
}
 // 调用 
test(10,{
	num1: Int, num2: Int -> num1 + num2 
}) 
// 结果为:18

说明:
invoke()函数:表示为通过函数变量调用自身,因为上面例子中的变量b是一个匿名函数

这里以Android中常见的按钮点击事件监听为例:
Kotlin写法:

mBtn.setOnClickListener(object: View.OnClickListener{ 
	override fun onClick(v: View?) {
	 		Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show() 
	}
})
使用Lambda表达式等同于
mBtn.setOnClickListener {
	Toast.makeText(this,"onClick",Toast.LENGTH_SHORT).show() 
}

Lambda表达式的特点:
◆ Lambda表达式总是被大括号括着
◆ 其参数(如果存在)在 -> 之前声明(参数类型可以省略)
◆ 函数体(如果存在)在 -> 后面。

Kotlin语言约定Lambda表达式的参数只有一个的时候可以使用it来使用此参数。it可表示为单个参数的隐式名称

fun test(num1 : Int, bool : (Int) -> Boolean) : Int{ 
return if (bool(num1)){
num1 
} else{
0
} 
 }
 println(test(10,{it > 5}))
 println(test(4,{it > 5}))
// 输出结果为:
// 10
// 0

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

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") 
}
/*输出结果:
key1  value1
 key2  value2
 key3  value3

 value1
 value2
 value3*/

闭包
说闭包前,我们先说一下作用域的问题。
我们知道变量的作用域,通常是两种:全局变量与局部变量

var n = 999
fun f1() {
 println(n) // 打印999 
}
 f1()

当我们调用f1函数的时候,会正确输出999,从而得知,在函数内部可以直接读取全局变量,如果想要从函数外部读取函数内的变量呢?我们知道这是无法读取的。

fun f1(){
 var n=999 
}
// println(n) // 报错!!! n是函数内局部变量,外部无法调用

那么,如何在外部调取局部的变量呢?
答案就是——闭包
闭包就是能够读取其他函数内部变量的函数。
作用
● 一个是前面提到的可以读取函数内部的变量
● 另一个就是让这些变量的值始终保持在内存中

举例说明:

fun test(b : Int): () -> Int{ 
	var a = 3 
	return fun() : Int{ 
		a++ 
		return a + b 
	} 
} 

val t = test(3)
println(t()) 
println(t()) 
println(t())
// 输出结果:
7
8
9

说明:
闭包就是在函数被创建的时候,存在的一个私有作用域,并且能够访问所有的父级作用域。每个功能模块我们都能够拆解到不同fun里,不同fun里的变量保持相互调用的可能性,相互独立还彼此不影响。

广义上来说,在Kotlin语言之中,函数、条件语句、控制流语句、花括号逻辑块、Lambda表达式都可以称之为闭包,但通常情况下,我们所指的闭包都是在说Lambda表达式。

自执行闭包
自执行闭包就是在定义闭包的同时直接执行闭包,一般用于初始化上下文环境。 例如:

{ x: Int, y: Int ->
println("${x + y}")
}(1, 3)

参考自:http://www.cnblogs.com/Jetictors/p/8647888.html

你可能感兴趣的:(Kotlin,kotlin,函数,lambda,闭包)