单表达式函数
某种情况函数只返回单个表达式,此时可省略花括号并在等号后指定函数体
fun area(x: Double, y: Double): Double = x * y;
fun area(x: Double, y: Double) = x * y; //编译器可自动判断返回值类型
参数个数可变的形参列表
Kotlin允许个数可变的形参可以处于形参列表的任意位置,但一个函数最多只能有一个个数可变的形参
fun fruit_name(vararg name:String) {
for(item in name){
println(item)
}
}
fun main(args : Array<String>) {
fruit_name("Hello", "World");
var fruit = arrayOf("apple", "pine", "applepine");
fruit_name(*fruit);//数组多个元素传入函数的变长参数需要在数组名前加*号
}
函数重载
Kotlin 允许定义多个同名函数,只要参数列表或返回值类型不同就行。
但是:函数重载只能通过参数列表区分。仅仅形参名不同、返回值类型不同或修饰符不同 都不能算作重载
局部函数
在函数体内部定义的函数,局部函数对外隐藏,只能在其封闭函数内有效,封闭函数也可返回局部函数,以便在其他作用域使用局部函数。如果封闭函数没有局部函数返回,那么局部函数将只能在封闭函数内部调用
fun cal(type: String, a: Int, b: Int): Int {
fun add(a: Int, b : Int): Int {
return a + b;
}
fun sub(a: Int, b : Int): Int {
return a - b;
}
when (type) {
"add" -> {
return add(a, b);
}
"sub" -> {
return sub(a, b);
}
}
return 0;
}
fun main(args : Array<String>) {
println(cal("add", 1, 2));
}
高阶函数
函数也是一等公民,因此函数本身也具备自己的类型。函数类型既可定义变量,也可用作函数形参类型,还可作为函数的返回值类型
fun func_one(a: Int, str: String) : String {
//TODO
}
//函数类型为(Int, String) -> String
fun func_two(a: Int, str: String) {
//TODO
}
//函数类型为(Int, String) -> Unit 或者 (Int, String)
在将函数作为变量进行值传递的时候,需要在前加上::,并且不能填写圆括号
fun sum(a: Int, b: Int): Int {
return a + b;
}
fun sub(a: Int, b: Int): Int {
return a - b;
}
fun main(args : Array<String>) {
var func : (Int, Int) -> Int;
func = ::sum;
println(func(1, 2));
func = ::sub;
println(func(9, 1));
}
我们还可以通过这种方式将函数作为参数进行传递
fun square(x: Int) = x * x;
fun inc(x: Int) = x + 1;
fun func(func_name: (Int) -> Int, x: Int):Int {
return func_name(x);
}
fun main(args : Array<String>) {
println(func(::inc, 100));
println(func(::square, 100));
}
当然我们还可以将函数作为结果返回(返回局部函数)
fun func(type: String): (Int) -> Int {
fun square(x: Int) = x * x;
fun inc(x: Int) = x + 1;
when (type) {
"square" -> {
return ::square;
}
"inc" -> {
return ::inc;
}
}
return ::inc;
}
fun main(args : Array<String>) {
println(func("square").invoke(100));
println(func("inc").invoke(100));
}
Lambda表达式
我们可以使用Lambda表达式来简化局部函数
fun func(type: String): (Int) -> Int {
when (type) {
"square" -> return {
x: Int -> x * x;
}
"inc" -> return {
x: Int -> x.inc();
}
}
return {x: Int -> x};
}
fun main(args : Array<String>) {
println(func("square").invoke(100));
println(func("inc").invoke(100));
}
Lambda表达式的特点
- 总是被大括号包裹
- 无需fun关键字,作为匿名函数
- 形参列表在->前声明,参数类型可省略(交给编译器判断)
- 函数体在->后声明
- 最后一个表达式默认作为Lambda表达式的返回值,无需return语句
Lambda表达式的调用
Lambda表达式的本质是功能更加灵活的代码块,因此Lambda表达式可以赋值给变量或者直接调用Lambda表达式。
//1
var squre = {
x: Int -> x * x;
}
println(squre(100));
//2
println({x: Int-> x + 1; }(100));
由于第二种写法没有将Lambda表达式赋值给任何变量直接调用,导致不知道形参列表的类型,所以必须要显式注明。
省略形参名
如果只有一个参数的时候可以省略参数名,Lambda表达式内使用it作为这个参数的标识符。
这种省略方式不能直接调用,因为编译器无法判断其参数列表的类型。
var func :(Int) -> Int= {it * it};
println(func(100));
尾随闭包
如果函数的最后一个参数是函数类型,如果打算传入一个lambda表达式作为参数,允许在圆括号之外指定Lambda表达式
fun FUNC(a:Int, b:Int, func: (Int, Int) -> Int):Int {
return func(a, b);
}
fun main(args : Array<String>) {
println(FUNC(1, 2){a:Int, b:Int -> a + b});
}
参数可变参数与函数参数
如果不将个数可变的形参放在形参列表的最后,那么就只能用命名参数的形式为可变参数之后的其他参数传入参数值
这里引发问题在于 将可变参数作为最后参数 还是 lambda表达式作为最后参数
Kotlin的约定: 如果调用函数时最后一个参数是Lambda表达式,则可将Lambda表达式存放在圆括号外面,这样就无需使用明明参数了
fun FUNC(vararg x:String, func:(x:String) -> Unit) {
for (item in x) {
func(item);
}
}
fun main(args : Array<String>) {
FUNC("JAVA", "KOTLIN", "ANDROID"){x -> println("HELLO WORLD! " + x + "!")};
}
匿名函数
Lambda表达式虽然简洁,但不能明确返回值类型。此时就可以使用匿名函数来取代Lambda表达式
var test = fun(x : Int, y: Int): Int {
return x + y;
}
println(test(2, 4));
return: Lambda的return返回其所在函数,而匿名函数的return是返回它本身
Lambda的 return 不允许执行,除非其他是个内联函数。由于非内联 Lambda表达式会额外生成一个函数对象
可以捕获上下文中的变量与常量
局部函数、Lambda表达式都可以访问或修改其所在上下文中的变量和常量。
Lambda表达式或匿名函数都会持有一个其所捕获的变量的副本。每返回一次Lambda表达式或者匿名函数都会新建一个副本,副本之间相互独立,不会相互影响。
fun FUNC(x: Int): () ->Unit {
fun FUNC_C() {
println(x);
}
return ::FUNC_C;
}
fun main(args : Array<String>) {
var func = FUNC(10086);
func();
}