【Kotlin】Kotlin 函数总结 ( 具名函数 | 匿名函数 | Lambda 表达式 | 闭包 | 内联函数 | 函数引用 )
文章目录
- 一、函数头声明
- 二、函数参数
- 1、默认参数值
- 2、具名参数
- 三、Unit 函数
- 四、TODO 函数抛出异常返回 Nothing 类型
- 五、反引号函数名
- 六、匿名函数
- 七、匿名函数的函数类型
- 八、匿名函数的隐式返回
- 九、匿名函数参数
- 十、匿名函数 it 关键字
- 十一、匿名函数变量类型推断
- 十二、匿名函数参数类型自动推断
- 十三、Lambda 表达式
- 十四、 函数参数为 Lambda 表达式
- 十五、Lambda 表达式作为参数的简略写法
- 1、Lambda 表达式作为唯一参数的简略写法
- 2、Lambda 表达式作为最后一个参数的简略写法
- 十六、内联函数
- 1、Lambda 表达式弊端
- 2、" 内联 " 机制避免内存开销
- 3、内联函数本质 - 编译时宏替换
- 4、内联函数不能递归
- 十七、普通函数代码示例
- 十八、内联函数代码示例
- 十九、函数引用作为函数参数
- 二十、函数类型作为函数返回值
- 二十一、闭包概念
- 二十二、Java 中函数作为参数的替代方案 ( 匿名内部类 )
一、函数头声明
函数头声明 格式如下 :
可见性修饰符 函数声明关键字 函数名 (函数参数) : 函数返回值类型
函数头示例 :
private fun hello(name:String, age:Int):String
- 可见性修饰符 :
private
- 函数声明关键字 :
fun
- 函数名 :
hello
- 函数参数 :
name:String, age:Int
- 函数返回值类型 :
String
代码示例 :
fun main() {val name = "Tom"val age = 18println(hello(name, age))
}private fun hello(name: String, age: Int): String {return "Name is $name, age $age, type is ${if(name == "Tom") "Cat" else "Mouse"}"
}
执行结果 :
Name is Tom, age 18, type is Cat
二、函数参数
1、默认参数值
默认参数值 : Kotlin 语言 中的 函数参数 , 可以 在定义时 指定参数默认值 ;
代码示例 :
fun main() {val name = "Tom"val age = 18println(hello())
}private fun hello(name: String = "Tom", age: Int = 18): String {return "Name is $name, age $age, type is ${if(name == "Tom") "Cat" else "Mouse"}"
}
执行结果 :
Name is Tom, age 18, type is Cat
2、具名参数
具名参数 : Kotlin 中的 函数参数顺序 必须按照定义的顺序传递 ,
如果使用 具名参数 , 可以不必按照参数顺序传递参数 ;
代码示例 : 在下面的代码中 , 函数参数为 name: String, age: Int
,
先传递 String 类型值 , 然后再传递 Int 类型值 ,
但是使用具名参数 后 , 可以 先传递 Int 参数 , 再传递 String 参数 ;
fun main() {println(hello(age = 18, name = "Tom"))
}private fun hello(name: String, age: Int): String {return "Name is $name, age $age, type is ${if(name == "Tom") "Cat" else "Mouse"}"
}
执行结果 :
Name is Tom, age 18, type is Cat
三、Unit 函数
Java 语言 中 没有返回值的函数 其 返回类型 是 void
;
Kotlin 语言 中 没有返回值的函数 其返回类型是 Unit
, 该函数又称为 Unit 函数 ;
Kotlin 语言中 推出 Unit 类型概念 , 是为了 兼容 泛型 概念 ,
如果 函数没有返回值 , 就可以 忽略该类型 , 返回 void
,
但是在 泛型 概念中 , 必须有一个确定的 类型 , 因此这里引入 Unit 类型 ;
代码示例 : 在下面代码的 hello()
函数的返回值类型是 Unit
类型 , 如果打印该返回值 , 打印结果为 kotlin.Unit
;
fun main() {println(hello())
}fun hello(): Unit {println("Hello")
}
执行结果 :
Hello
kotlin.Unit
四、TODO 函数抛出异常返回 Nothing 类型
在 Kotlin 中 有一种函数 TODO 函数 ,
TODO 函数 唯一的作用 就是 抛出异常 ,
该函数 执行永远失败 , 并且 返回 Nothing 类型 ;
TODO 函数原型如下 :
/*** 总是抛出[NotImplementedError],表示操作未实现。** @param reason一个解释为什么缺少实现的字符串。*/
@kotlin.internal.InlineOnly
public inline fun TODO(reason: String): Nothing = throw NotImplementedError("An operation is not implemented: $reason")
代码示例 :
fun main() {TODO("TODO 抛出异常")
}
执行结果 :
Exception in thread "main" kotlin.NotImplementedError: An operation is not implemented: TODO 抛出异常at HelloKt.main(Hello.kt:2)at HelloKt.main(Hello.kt)
五、反引号函数名
Java 的函数名要求 :
- 只能由 字母数字下划线 构成
- 首字母并不能是数字
- 不能是关键字
Kotlin 中 函数名 可以使用 空格 , 特殊字符 , 关键字 , 前提是 该函数名 必须使用 反引号 ;
注意 Kotlin 和 Java 中的关键字不同 , 不管是哪个语言的关键字 , 都不能作为函数名 , 但是如果将关键字 使用反引号 括起来 , 就可以使用其作为函数名 ;
代码示例 :
fun main() {`~!@# Hello World %^&*`()
}fun `~!@# Hello World %^&*`(){println("Hello World")
}
执行结果 :
六、匿名函数
声明函数时 , 没有 函数名 的函数 是 匿名函数 ;
匿名函数 可以作为 函数参数 , 也可以作为 函数返回值 ;
匿名函数 可以 定制修改 已有的 函数 , 如 : 标准库中的函数 ;
Kotlin 中对 CharSequence
类进行了扩展 , 定义了 count(predicate: (Char) -> Boolean)
扩展函数 , 接收一个 (Char) -> Boolean
类型的函数 , 用于 返回匹配给定 匿名函数 的字符数 ;
/*** 返回匹配给定[谓词 predicate 匿名函数]的字符数。*/
public inline fun CharSequence.count(predicate: (Char) -> Boolean): Int {var count = 0for (element in this) if (predicate(element)) ++countreturn count
}
代码示例 : 在下面的代码中 , 传入了 匿名函数
{ letter->letter == 'l'
}
作为参数 , 其作用是 返回 "Hello"
字符串中 , 字符符合 letter == 'l'
要求的字符个数 ;
fun main() {// 统计字符串长度val toalCount = "Hello".count()println("toalCount = $toalCount")// 统计字符串中 l 字符长度val toalLCount = "Hello".count({ letter->letter == 'l'})println("toalLCount = $toalLCount")
}
执行结果 :
toalCount = 5
toalLCount = 2
七、匿名函数的函数类型
匿名函数 可以作为 变量 赋值给 函数类型变量 ,
可以作为 函数参数 传递给函数 ,
因此 , 匿名函数 与 变量 一样 , 也存在 对应的 函数类型 ;
函数类型 由 参数 和 返回值 决定 ;
有 相同 参数顺序 , 参数个数 和 返回值类型 的函数 , 其 函数类型相同 ;
如上个章节 , 扩展函数 CharSequence.count
接收的匿名函数参数 predicate
, 其函数类型是 (Char) -> Boolean
;
public inline fun CharSequence.count(predicate: (Char) -> Boolean): Int
代码示例 : 声明一个函数类型变量 , 然后为其赋值 , 最后执行上述函数 ;
fun main() {// 声明 函数类型 变量val helloFun: ()->String// 为 函数类型变量 赋值一个 匿名函数helloFun = {"Hello World"}// 执行 函数类型 变量对应的 函数val str = helloFun()println(str)
}
执行结果 :
Hello World
八、匿名函数的隐式返回
普通函数 返回值 , 都是 显示返回 , 如 : 使用 return
关键字 , 返回返回值 ;
匿名函数 的 返回值 不需要使用 return
关键字 ,
匿名函数 可以 隐式 返回 函数体最后一行语句 ;
代码示例 : 在匿名函数中 , 第一行是 Int
值 , 第二行是 Boolean
值 , 第三行是 String
值 , 最后返回的是最后一行 String
值 ;
fun main() {// 声明 函数类型 变量, 并为其赋值 匿名函数val helloFun: ()->String = {123true"Hello World"}// 执行 函数类型 变量对应的 函数println(helloFun())
}
执行结果 :
九、匿名函数参数
匿名函数 可以不带参数 , 也可以带多个参数 ;
不带参数的匿名函数 :
// 声明 函数类型 变量, 并为其赋值 匿名函数val helloFun: ()->String = {"Hello World"}
带参数的匿名函数 : 匿名函数 的 参数类型 放在 函数类型 定义中 , 参数名 放在 函数体 内 ;
// 声明 函数类型 变量, 并为其赋值 匿名函数val helloFun: (Int)->String = { age ->"Hello World $age"}
上面的 匿名参数 , 函数类型 是 (Int)->String
,
函数类型 中 , 只有参数类型 , 没有参数名 ,
函数体中 age ->
中 age
就是对应的 Int 类型参数的 参数名 ,
函数体中 , 只有参数名 , 没有参数类型 ;
代码示例 :
fun main() {// 声明 函数类型 变量, 并为其赋值 匿名函数val helloFun: (Int)->String = { age ->"Hello World $age"}// 调用该 (Int)->String 类型的匿名函数, 传入 Int 值作为参数println(helloFun(18))
}
执行结果 :
十、匿名函数 it 关键字
如果 匿名函数 只有 1 个函数参数 , 在 匿名函数 的 函数体 中 , 可以 省略 函数名 声明 , 使用 it 关键字 代替 ;
代码示例 : 在下面的 匿名函数中 , 只有 一个 Int 类型的函数参数 , 在函数体中可以省略 age ->
参数名 声明 , 可以 使用默认的 it 关键字 作为 参数名 ;
fun main() {// 声明 函数类型 变量, 并为其赋值 匿名函数val helloFun: (Int)->String = {"Hello World $it"}// 调用该 (Int)->String 类型的匿名函数, 传入 Int 值作为参数println(helloFun(18))
}
执行结果 :
Hello World 18
十一、匿名函数变量类型推断
定义变量 时 , 如果将变量值 直接赋值给该变量 , 那么就可以 不用显示声明该变量的类型 ;
下面的代码中 , 定义 name 变量 , 为其 赋值 “Tom” 字符串 String 类型变量值 , 则 该变量被自动推断为 String 类型变量 ;
var name = "Tom"
如果 变量没有赋值 , 则声明变量时 , 必须显示声明该变量的类型 ;
var name: String
如果定义一个 函数类型 变量 , 将 匿名函数 作为变量值 赋值给该变量 , 此时可以 不需要显示声明 函数类型 变量的值 ;
下面的代码中的 函数类型 : ()->String
可以省略 , 由 类型推断 来确定 helloFun 只读变量的值 ;
val helloFun: ()->String = {val name = "Tom""Hello World, $name"}
代码示例 : 如下代码中 , helloFun
变量没有设置变量类型 , 其类型由 赋值给 该变量的 匿名函数 的 类型自动推断得来 , 匿名函数类型为 ()->String
类型 ;
fun main() {val helloFun = {val name = "Tom""Hello World, $name"}println(helloFun())
}
十二、匿名函数参数类型自动推断
如果 需要 使用 自动类型推断 确定 匿名函数 的 参数类型 ,
则在 匿名函数 的 函数体中 , 必须 显示声明 匿名函数 的 变量名 和 变量类型 ;
匿名函数 返回值 类型 , 是根据 匿名函数 函数体 中 最后一行表达式的值 进行自动推断的 ;
代码示例 : 在下面的函数中 , 匿名函数的函数体中 , 使用 变量名: 变量类型 -> , name: String, age: Int ->
, 显示声明了匿名函数的 参数类型 , 这样就可以使用 类型推断 , 自动推断出 匿名函数 的参数类型 ;
该匿名函数 函数体 最后一行表达式 的 类型 是 String 类型 , 其 返回值类型就是 String 类型 ;
fun main() {val helloFun = { name: String, age: Int ->"Hello World, $name, $age"}println(helloFun("Tom", 18))
}
执行结果 :
Hello World, Tom, 18
如果 不使用 匿名函数 类型推断 ,
则在 函数变量 声明时 , 确定 函数参数 类型 ,
在 匿名函数 函数体 中 , 确定 函数参数名 即可 ,
示例代码如下 :
fun main() {val helloFun: (String, Int)->String = { name, age ->"Hello World, $name, $age"}println(helloFun("Tom", 18))
}
执行结果 :
Hello World, Tom, 18
十三、Lambda 表达式
匿名函数 又称为 Lambda 表达式 , 匿名函数的 返回值 是 Lambda 结果 ;
十四、 函数参数为 Lambda 表达式
在 定义函数 时 , 函数的参数 可以是 函数类型的变量 ,
可以传递一个 匿名函数 作为 函数参数 ;
匿名函数 就是 Lambda 表达式 ;
代码示例 : 在下面的代码中 ,
- 函数参数 :
studentDoSomething
函数的 第三个参数为 action: (String, Int) -> String
,
其参数类型为 (String, Int) -> String
, 是一个 函数类型 ;
- 函数类型变量 :
在 main
函数中 , 定义函数类型变量 actionFun
, 之后 该变量会作为函数参数传递给函数 ,
同时使用了 匿名函数 , 为该函数类型变量 actionFun
赋值 ;
- 匿名函数类型自动推断 :
在该 匿名函数中 , 使用了 自动类型推断 , 在函数体中的参数列表 ,
声明了 完整的 参数名:参数类型 , name: String, age: Int ->
;
- 函数变量作函数参数 :
在最后 , 将 函数类型 变量 actionFun
传递给了 studentDoSomething
函数 , 作为其第三个参数使用 ;
fun main() {// 定义函数类型变量, 之后作为函数参数传递给函数val actionFun = { name: String, age: Int ->"student $name $age years old, say hello"}// 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作studentDoSomething("Tom", 18, actionFun);
}fun studentDoSomething(name: String, age: Int,action: (String, Int) -> String) {val act = action(name, age);println(act)
}
执行结果 :
student Tom 18 years old, say hello
十五、Lambda 表达式作为参数的简略写法
1、Lambda 表达式作为唯一参数的简略写法
如果 Lambda 表达式 作为 函数参数 , 并且 该参数是 唯一参数 , 那么 Lambda 表达式外面的圆括号可以省略 ;
代码示例 :
fun main() {// 调用 doSomething 函数, 传入 Lambda 表达式 / 匿名函数doSomething({"Hello World"})
}fun doSomething(action: () -> String) {val act = action();println(act)
}
此时将鼠标移动到 Lambda 表达式 上 , 也就是匿名函数中 , 会提示
Lambda argument should be moved out of parentheses
参数应该移出圆括号
Kotlin 建议我们移除 Lambda 表达式 外面的圆括号 ;
修改后的代码示例 :
fun main() {// 调用 doSomething 函数, 传入 Lambda 表达式 / 匿名函数doSomething {"Hello World"}
}fun doSomething(action: () -> String) {val act = action();println(act)
}
执行结果 :
2、Lambda 表达式作为最后一个参数的简略写法
如果 Lambda 表达式 作为 函数参数 , 并且 该参数是 若干参数的最后一个参数 , 那么 Lambda 表达式可以提到括号外面 ;
在上一个章节的如下代码 , 可以直接 将 匿名函数 作为函数参数进行传递 , 不必使用 函数类型 变量名作为参数 ,
fun main() {// 定义函数类型变量, 之后作为函数参数传递给函数val actionFun = { name: String, age: Int ->"student $name $age years old, say hello"}// 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作studentDoSomething("Tom", 18, actionFun);
}
直接使用匿名函数作为函数参数 的效果 :
fun main() {// 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作studentDoSomething("Tom", 18, { name: String, age: Int ->"student $name $age years old, say hello"})
}
匿名函数 , 也就是 Lambda 表达式 作为最后一个参数 , 可以提取到括号外面 , 代码效果如下 :
fun main() {// 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作studentDoSomething("Tom", 18) { name: String, age: Int ->"student $name $age years old, say hello"}
}
最终的简化后的代码示例 :
fun main() {// 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作studentDoSomething("Tom", 18) { name: String, age: Int ->"student $name $age years old, say hello"}
}fun studentDoSomething(name: String, age: Int,action: (String, Int) -> String) {val act = action(name, age);println(act)
}
执行结果 :
student Tom 18 years old, say hello
十六、内联函数
1、Lambda 表达式弊端
Lambda 表达式弊端 :
Lambda 表达式 的 灵活使用 , 是以 牺牲内存开销为代价的 ;
在 Java 虚拟机中 , Lambda 表达式 是以 实例对象 的形式 , 存储在堆内存中的 , 这就产生了内存开销 ;
2、" 内联 " 机制避免内存开销
" 内联 " 机制避免内存开销 :
在 Kotlin 语言中提供了一种 " 内联 " 机制 ,
解决了上面的 Lambda 表达式的 内存开销 问题 ,
将 使用 Lambda 表达式 作为参数的函数 定义为 inline 内联函数 ,
Java 虚拟机就 不会再为 lambda 表达式 在堆内存中 创建 实例对象 了 ,
这样就 避免了 Lambda 表达式 的内存开销 ;
3、内联函数本质 - 编译时宏替换
内联函数使用 :
在使用 Lambda 表达式的时候 ,
Kotlin 编译器直接将 inline 内联函数 的 函数体 直接拷贝到 使用位置 ;
内联函数 类似于 C 语言中的 预编译指令 宏定义 , 在编译时直接替换拷贝宏定义内容 ;
Kotlin 中的 内联函数 也是一种 编译时 进行 宏替换的操作 ;
4、内联函数不能递归
内联函数不能递归 :
如果 将函数 定义为 内联函数 ,
则该函数 不能进行递归操作 ,
递归操作 会导致 函数体的 无限复制粘贴 ,
编译器会报警 ;
十七、普通函数代码示例
代码示例 : 下面的代码中 studentDoSomething
是普通函数 ;
fun main() {// 定义函数类型变量, 之后作为函数参数传递给函数val actionFun = { name: String, age: Int ->"student $name $age years old, say hello"}// 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作studentDoSomething("Tom", 18, actionFun);
}fun studentDoSomething(name: String, age: Int,action: (String, Int) -> String) {val act = action(name, age);println(act)
}
将字节码转换为 Java 代码内容如下 :
import kotlin.Metadata;
import kotlin.jvm.functions.Function2;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;@Metadata(mv = {1, 1, 16},bv = {1, 0, 3},k = 2,d1 = {"\u0000\u001c\n\u0000\n\u0002\u0010\u0002\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\b\n\u0000\n\u0002\u0018\u0002\n\u0000\u001a\u0006\u0010\u0000\u001a\u00020\u0001\u001a0\u0010\u0002\u001a\u00020\u00012\u0006\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u00062\u0018\u0010\u0007\u001a\u0014\u0012\u0004\u0012\u00020\u0004\u0012\u0004\u0012\u00020\u0006\u0012\u0004\u0012\u00020\u00040\b¨\u0006\t"},d2 = {"main", "", "studentDoSomething", "name", "", "age", "", "action", "Lkotlin/Function2;", "KotlinDemo"}
)
public final class HelloKt {public static final void main() {Function2 actionFun = (Function2)null.INSTANCE;studentDoSomething("Tom", 18, actionFun);}// $FF: synthetic methodpublic static void main(String[] var0) {main();}public static final void studentDoSomething(@NotNull String name, int age, @NotNull Function2 action) {Intrinsics.checkParameterIsNotNull(name, "name");Intrinsics.checkParameterIsNotNull(action, "action");String act = (String)action.invoke(name, age);boolean var4 = false;System.out.println(act);}
}
十八、内联函数代码示例
代码示例 : 下面的代码中 studentDoSomething
是内联函数 ;
fun main() {// 定义函数类型变量, 之后作为函数参数传递给函数val actionFun = { name: String, age: Int ->"student $name $age years old, say hello"}// 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作studentDoSomething("Tom", 18, actionFun);
}inline fun studentDoSomething(name: String, age: Int,action: (String, Int) -> String) {val act = action(name, age);println(act)
}
将字节码转换为 Java 代码内容如下 :
import kotlin.Metadata;
import kotlin.jvm.functions.Function2;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;@Metadata(mv = {1, 1, 16},bv = {1, 0, 3},k = 2,d1 = {"\u0000\u001c\n\u0000\n\u0002\u0010\u0002\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\b\n\u0000\n\u0002\u0018\u0002\n\u0000\u001a\u0006\u0010\u0000\u001a\u00020\u0001\u001a3\u0010\u0002\u001a\u00020\u00012\u0006\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u00062\u0018\u0010\u0007\u001a\u0014\u0012\u0004\u0012\u00020\u0004\u0012\u0004\u0012\u00020\u0006\u0012\u0004\u0012\u00020\u00040\bH\u0086\b¨\u0006\t"},d2 = {"main", "", "studentDoSomething", "name", "", "age", "", "action", "Lkotlin/Function2;", "KotlinDemo"}
)
public final class HelloKt {public static final void main() {Function2 actionFun = (Function2)null.INSTANCE;String name$iv = "Tom";int age$iv = 18;int $i$f$studentDoSomething = false;String act$iv = (String)actionFun.invoke(name$iv, Integer.valueOf(age$iv));boolean var5 = false;System.out.println(act$iv);}// $FF: synthetic methodpublic static void main(String[] var0) {main();}public static final void studentDoSomething(@NotNull String name, int age, @NotNull Function2 action) {int $i$f$studentDoSomething = 0;Intrinsics.checkParameterIsNotNull(name, "name");Intrinsics.checkParameterIsNotNull(action, "action");String act = (String)action.invoke(name, age);boolean var5 = false;System.out.println(act);}
}
十九、函数引用作为函数参数
函数 作为参数 , 有两种方式 :
- 传递 Lambda 表达式 , 也就是 匿名函数 作为参数值 ;
- 传递 函数引用 作为参数值 ;
函数引用 可以将 具名函数 转为 函数的参数值 , 只要可以使用 Lambda 表达式 参数的地方 , 就可以使用 函数引用 进行替代 ;
函数引用格式 : 两个冒号 加上 函数名 , 就是函数引用 ;
::函数名
如下 doSomething
函数的 函数引用 是 ::doSomething
;
fun doSomething(name: String, age: Int): String {return "student $name $age years old, say hello"
}
具名函数 与 匿名函数 相对 , 具名函数 是有 函数名的函数 , 匿名函数 没有函数名 ;
代码示例 : 在下面的代码中 ,
首先使用 actionFun
函数类型变量 作为 studentDoSomething
函数的参数 , 该变量的值是一个 匿名函数 Lambda 表达式 ,
然后使用 doSomething
函数的 函数引用 ::doSomething
作为 studentDoSomething
函数的参数 ,
使用 匿名函数 Lambda 表达式 作为参数 与 使用 函数引用 作为参数 , 其效果是相同的 ;
fun main() {// 定义函数类型变量, 之后作为函数参数传递给函数// 该匿名函数变量, 可以作为参数val actionFun = { name: String, age: Int ->"student $name $age years old, say hello"}// 调用 studentDoSomething 函数, 输入姓名, 年龄, 执行的操作// 使用匿名函数 Lambda 表达式作为参数studentDoSomething("Tom", 18, actionFun);// 使用函数引用作为参数studentDoSomething("Jerry", 17, ::doSomething);
}fun studentDoSomething(name: String, age: Int,action: (String, Int) -> String) {val act = action(name, age);println(act)
}fun doSomething(name: String, age: Int): String {return "student $name $age years old, say hello"
}
执行结果 :
student Tom 18 years old, say hello
student Jerry 17 years old, say hello
二十、函数类型作为函数返回值
函数 的 返回值类型 , 也可以是 函数类型 ;
也就是说 匿名函数 , Lambda 表达式 可以作为 函数的返回值 ;
代码示例 : 下面的代码中 ,
returnFun
函数的返回值 是一个函数类型 (String)->String
, 返回的是一个 匿名函数 Lambda 表达式 ;
使用 var fun0
变量 接收 上述函数 返回的 Lambda 表达式 , 并执行该 匿名函数 ;
fun main() {// 接收函数类型的返回值var fun0 = returnFun();// 执行 返回的 函数var str = fun0("Tom")println(str)
}// 函数的返回值 是函数类型
fun returnFun(): (String)->String {return { name: String ->"Hello $name"}
}
执行结果 :
Hello Tom
二十一、闭包概念
匿名函数 就是 Lambda 表达式 , 同时也是 闭包 , 三者的是相同的概念 ;
闭包意义 :
在 Java 中 , 通过 Package 包 , Class 类 , 将作用域区分开 ,
将变量 定义在 不同的 包 或 类中 , 可以很明显的将作用域区分开 ;
如果没有 Class 类 , 在一个 脚本文件 中 , 如 Kotlin 的脚本 , 就需要 使用 函数 进行作用域区分 ,
将一个作用域内的相关代码 , 都写在一个函数中 , 这样就可以将作用域分区分开 ;
匿名函数 作用域 :
在 匿名函数 / Lambda 表达式 / 闭包 中 , 可以 引用 作用域 之外的变量 ,
如 :
在 函数 A 中定义了 匿名函数 B , 则 在 匿名函数 B 中 , 可以引用 函数 A 中的变量 和 全局变量 ;
在 匿名函数 B 中定义了 匿名函数 C , 则 在 匿名函数 C 中 , 可以引用 匿名函数 B , 函数 A 中的变量 和 全局变量 ;
在 函数 A 中 , 不能引用 匿名函数 B 和 匿名函数 C 中的变量 ;
在 匿名函数 B 中 , 不能引用 匿名函数 C 中的变量 ;
高级函数概念 :
在 函数式编程 中 , 经常使用 高级函数 , 高级函数 是使用 函数类型变量 作为 参数 或 返回值 的 函数 ;
使用 匿名函数 / Lambda 表达式 / 闭包 作为 参数 / 返回值 的函数 是 高级函数 ;
Kotlin 中经常使用 链式调用 , 就是 函数式编程 的风格 ;
二十二、Java 中函数作为参数的替代方案 ( 匿名内部类 )
Kotlin 中引入 函数类型 , 将 匿名函数 / Lambda 表达式 / 闭包 作为 函数参数 或 返回值 , 可以写出 更加灵活的代码 ;
Java 8 开始 支持 Lambda 表达式 , 但是 不支持 函数 作为参数 , 也 不支持将 函数 赋值给变量 ;
Java 语言 将 函数 作为参数 的 替代方案 是 : 使用 匿名内部类 作为函数参数 ;
代码示例 : Java 代码中 , 接收 OnClickListener listener
类型的接口类型参数 , 在实际使用时 , 可以传入一个匿名内部类作为参数 , 将函数实现在匿名内部类中 ;
public class JavaMethod {public static void main(String[] args) {setClick(new OnClickListener() {@Overridepublic void click() {System.out.println("On Click");}});}public interface OnClickListener {void click();}public static void setClick(OnClickListener listener) {listener.click();}
}
执行结果 :
【Kotlin】Kotlin 函数总结 ( 具名函数 | 匿名函数 | Lambda 表达式 | 闭包 | 内联函数 | 函数引用 )相关推荐
- python lambda 逻辑_Python之lambda表达式和内置函数
lambda表达式其实就是简化的函数表达式. 它只用于处理简单逻辑, 它会自动return数据 通常定义一个函数,按照以下形式:def func(arg): return arg +1 result ...
- Python之lambda表达式和内置函数
lambda表达式其实就是简化的函数表达式. 它只用于处理简单逻辑, 它会自动return数据 通常定义一个函数,按照以下形式: 1 2 3 4 5 def func(arg): ret ...
- C++类的内联成员函数应放在哪
今天复习C++ Primer的时候,看到了关于C++类的内联成员函数的放置,应该放在头文件中.那么这到底是为什么 呢?仅仅是一种代码规范问题还是必须这样做呢? 下面我就来讲讲我自己的理解吧.要彻底理解 ...
- C++(13)--函数的进阶:内联、传递引用、参数默认值、重载、函数模板
模块化编程--函数的进阶 1.内联函数 1.1 inline基本情况 1.2 inline 的前世今生-带参的宏替换 2.传递引用(重点) 2.1引用.理由.注意事项 2.3 交换两个变量的数值 3. ...
- SQL Server内联表值函数
In this article series, we will find basics and common usage scenarios about the inline table-valued ...
- 抽象数据类型(顺序栈)、断言、包含头文件、内联函数、非内联成员函数[C++ In Action][4]...
1. C++中的接口与实现思想, 即类的定义.数据成员的定义.函数原型在接口文件中进行, 实现代码放在实现文件中 2. 函数调用开销:调用前要先保存寄存器,并在返回点恢复:复制实参:程序必须转入一个新 ...
- 多语句表值函数与内联表值函数区别?
有几个例子要展示,以防万一: 内联表值 CREATE FUNCTION MyNS.GetUnshippedOrders() RETURNS TABLE AS RETURN SELECT a.SaleI ...
- c++ 类的内联成员函数
c++ 类的内联成员函数 1.什么是内联函数 在类的声明内部声明和定义的函数叫做内联成员函数,如下面例子,函数setA是普通的成员函数,函数setB是隐式的内联函数,函数setC是显式的内联函数. / ...
- Python学习 Day8-2 python的三元表达式(三目运算符)、lambda表达式、内置函数
Python中的三元表达式(三目运算符) 语法:为真时的结果 if 判断条件 else 为假时的结果(注意,没有冒号) >>print(1 if 5 > 3 else 0) 1 Py ...
最新文章
- Altium Designer 发现的机密
- Spring boot优点
- python3的print函数
- 简单的Session案例 —— 一次性验证码
- 嵌入式系统之操作系统篇
- BGP——本地优先级选路+BGP路由水平分割机制(讲解+配置命令)
- 漫画:博弈论系列 之 囚徒困境
- 供应链金融管理系统-汇新云
- jsonp 跨域 java_浅析 JSONP 跨域原理
- 【黑帽SEO案例分析】10天爱站从0到6
- ieeetran_IEEEtran BibTex样式
- 《阿里云服务器教程2》:如何远程连接linux系统阿里云服务器ECS
- 微信小程序上传Excel文本文件功能
- ccf-20161203--权限查询
- PHP面试题(遇到的)
- 线下停摆,线上狂欢,疫情下“云健身”火了!
- maya2018英文翻译_maya2018英文怎么切换中文?
- 数字藏品交易平台如何上架数字藏品?
- maven常见构建源码命令
- 滴滴投资人被滴滴司机殴打;罗振宇吐槽淘宝假货;上海布局研发新一代“中国芯”丨价值早报...
热门文章
- pycharm专业版 激活+汉化
- geo mysql_GEO数据库及应用场景介绍
- 数据分析实战项目-用户行为分析(Python)
- 攻防世界-misc-流量分析1
- Python学习八:数据库编程接口
- 25. Linux中的web服务器Apache
- [BUUCTF-pwn] wdb_2020_1st_boom1
- mysql内循环是什么人_mysql循环
- MySQL8.0.27安装后,使用CMD无法启动mysql服务
- 报错:pymysql.err.IntegrityError: (1062, “Duplicate entry ‘1‘ for key ‘mm.PRIMARY‘“)