kotlin函数和Lambda表达式——>内联函数
1.内联函数
使用高阶函数会带来一些运行时的效率损失:每一个函数都是一个对象,并且会捕获一个闭包。即那些 在函数体内会访问到的变量。内存分配(对于函数对象和类)和虚拟调用会引入运行时间开销。
但是在许多情况下通过内联化 lambda 表达式可以消除这类的开销。下述函数是这种情况的很好的例 子。即 lock() 函数可以很容易地在调用处内联。考虑下面的情况
lock(l) { foo() }
编译器没有为参数创建一个函数对象并生成一个调用。取而代之,编译器可以生成以下代码:
l.lock()
try {
foo()
} finally {
l.unlock()
}
为了让编译器这么做,我们需要使用 inline 修饰符标记 lock() 函数:
inline fun <T> lock(lock: Lock, body: () -> T): T { ...... }
inline 修饰符影响函数本身和传给它的 lambda 表达式:所有这些都将内联到调用处。
内联可能导致生成的代码增加;不过如果我们使用得当(即避免内联过大函数),性能上会有所提升,尤 其是在循环中的“超多态(megamorphic)”调用处。
2.禁用内联
如果希望只内联一部分传给内联函数的 lambda 表达式参数,那么可以用 noinline 修饰符标记不希望内联的函数参数
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { ...... }
可以内联的 lambda 表达式只能在内联函数内部调用或者作为可内联的参数传递,但是 noinline的可以以任何我们喜欢的方式操作:存储在字段中、传送它等等。
需要注意的是,如果一个内联函数没有可内联的函数参数并且没有具体化的类型参数,编译器会产生一 个警告,因为内联这样的函数很可能并无益处(如果你确认需要内联,则可以用@Suppress("NOTHING_TO_INLINE") 注解关掉该警告)。
非局部返回
在 Kotlin 中,我们只能对具名或匿名函数使用正常的、非限定的 return 来退出。这意味着要退出一个 lambda表达式,我们必须使用一个标签,并且在lambda表达式内部禁止使用裸 return,因为lambda 表达式不能使包含它的函数返回:
fun foo() {
ordinaryFunction {
return // 错误:不能使 `foo` 在此处返回
}
}
但是如果 lambda 表达式传给的函数是内联的,该 return 也可以内联,所以它是允许的: inline fun inlined(block: () -> Unit) { println("hi!") }
fun foo() {
inlined {
return // OK:该 lambda 表达式是内联的
}
}
}
这种返回(位于 lambda 表达式中,但退出包含它的函数)称为非局部返回。我们习惯了在循环中用这种结构,其内联函数通常包含
fun hasZeros(ints: List<Int>): Boolean {
ints.forEach {
if (it == 0) return true // 从 hasZeros 返回
}
return false
}
请注意,一些内联函数可能调用传给它们的不是直接来自函数体、而是来自另一个执行上下文的 lambda 表达式参数,例如来自局部对象或嵌套函数。在这种情况下,该 lambda 表达式中也不允许非局 部控制流。为了标识这种情况,该 lambda 表达式参数需要用 crossinline 修饰符标记:
inline fun f(crossinline body: () -> Unit) {
val f = object : Runnable {
override fun run() = body()
}
// ......
}
break和continue在内联的 lambda 表达式中还不可用,但我们也计划支持它们。
3.具体化的类型参数
有时候我们需要访问一个作为参数传给我们的一个类型:
fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
var p = parent
while (p != null && !clazz.isInstance(p)) {
p = p.parent
}
@Suppress("UNCHECKED_CAST")
return p as T?
}
在这里我们向上遍历一棵树并且检测每个节点是不是特定的类型。这都没有问题,但是调用处不是很优雅:
treeNode.findParentOfType(MyTreeNode::class.java)
我们真正想要的只是传一个类型给该函数,即像这样调用它
treeNode.findParentOfType<MyTreeNode>()
为能够这么做,内联函数支持具体化的类型参数,于是我们可以这样写
inline fun <reified T> TreeNode.findParentOfType(): T? {
var p = parent
while (p != null && p !is T) {
p = p.parent
}
return p as T?
}
我们使用 reified 修饰符来限定类型参数,现在可以在函数内部访问它了,几乎就像是一个普通的类 一样。由于函数是内联的,不需要反射,正常的操作符如 !is 和 as 现在都能用了。此外,我们还可以按 照上面提到的方式调用它:myTree.findParentOfType<MyTreeNodeType>() 。
虽然在许多情况下可能不需要反射,但我们仍然可以对一个具体化的类型参数使用它:
inline fun <reified T> membersOf() = T::class.members
fun main(s: Array<String>) {
println(membersOf<StringBuilder>().joinToString("\n"))
}
普通的函数(未标记为内联函数的)不能有具体化参数。不具有运行时表示的类型(例如非具体化的类型 参数或者类似于 Nothing 的虚构类型)不能用作具体化的类型参数的实参。
4.内联属性(自1.1起)
inline 修饰符可用于没有幕后字段的属性的访问器。你可以标注独立的属性访问器:
val foo: Foo
inline get() = Foo()
var bar: Bar
get() = ......
inline set(v) { ...... }
你也可以标注整个属性,将它的两个访问器都标记为内联
inline var bar: Bar
get() = ......
set(v) { ...... }
在调用处,内联访问器如同内联函数一样内联
5.公有API内联函数的限制
当一个内联函数是 public 或 protected 而不是 private 或 internal 声明的一部分时,就会 认为它是一个模块级的公有 API。可以在其他模块中调用它,并且也可以在调用处内联这样的调用。
这带来了一些由模块做这样变更时导致的二进制兼容的⻛险⸺声明一个内联函数但调用它的模块在 它修改后并没有重新编译。
为了消除这种由非公有 API 变更引入的不兼容的⻛险,公有 API 内联函数体内不允许使用非公有声明, 即,不允许使用 private 与 internal 声明以及其部件。
一个 internal 声明可以由 @PublishedApi 标注,这会允许它在公有 API 内联函数中使用。当一 个 internal 内联函数标记有 @PublishedApi 时,也会像公有函数一样检测其函数体。
kotlin函数和Lambda表达式——>内联函数的更多相关文章
- c++中函数的参数传递,内联函数和默认实参的理解
1.参数传递 1)函数调用时,c++中有三种传递方法:值传递.指针传递.引用传递. 给函数传递参数,遵循变量初始化规则.非引用类型的形参一相应的实参的副本初始化.对(非引用)形参的任何修改仅作用域局部 ...
- 转-C++之虚函数不能定义成内联函数的原因
转自:https://blog.csdn.net/flydreamforever/article/details/61429140 在C++中,inline关键字和virtual关键字分别用来定义c+ ...
- Python函数与lambda 表达式(匿名函数)
Python函数 一.函数的作用 函数是组织好的,可重复使用的,用来实现单一或相关联功能的代码段 函数能提高应用的模块性和代码的重复利用率 python 内置函数:https://docs.pytho ...
- 函数新特性、内联函数、const详解
一.函数回顾与后置返回类型 函数定义中,形参如果在函数体内用不到的话,则可以不给形参变量名字,只给其类型. 函数声明时,可以只有形参类型,没有形参名 把函数返回类型放到函数名字之前,这种写法,叫前置返 ...
- 堆(stack) 之 c 和 c++模板实现(空类默认成员函数 初谈引用 内联函数)
//stack 的基本操作 #include <iostream> using namespace std; const int maxn = 3; typedef struct Stac ...
- 预处理、const、static与sizeof-为什么不把所有的函数都定义成内联函数
1:内联是以代码膨胀(复制)为代价的,仅仅省去了函数调用的开销,从而提高函数的执行效率.如果执行函数体内代码的时间相比于函数调用的开销较大,那么效率的收获会很小.另一方面,每一处内联函数的调用都要复制 ...
- 第三天 函数 三元运算 lambda表达式 内置函数 文件操作
面向过程: 直接一行一行写代码,遇到重复的内容复制黏贴. 不利于代码阅读 代码没有复用 面向对象 将代码块定义为函数,以后直接调用函数 增强了复用性 函数的定义方法 def 函数名(传递参数): 函数 ...
- 特殊用途语言特性——默认参数、内联函数和constexptr函数
1 默认实参 某些函数有这样一些参数,在函数的很多次调用中它们都被赋予一个相同的值,此时,我们把这个反复出现的值称为函数的默认实参.调用含有默认实参的函数时,可以包含该实参,也可以省略该实参. 我们可 ...
- C++内联函数与宏定义
用内联取代宏: 1.内联可调试: 2.可进行类型安全检查或自动类型转换: 3.可访问成员变量. 另外,定义在类声明中的成员函数自动转化为内联函数. 文章(一) 内联函数与宏定义 在C中,常用预处理语句 ...
- 内联函数 inline 漫谈
内联函数存在的结论是: 引入内联函数是为了解决函数调用效率的问题 由于函数之间的调用,会从一个内存地址调到另外一个内存地址,当函数调用完毕之后还会返回原来函数执行的地址.函数调用会有一定的时间开销,引 ...
随机推荐
- 【RabbitMQ】13 消息补偿 & 幂等保障
https://www.bilibili.com/video/BV15k4y1k7Ep?p=34 我靠,幂等保障就是乐观锁控制啊 https://www.bilibili.com/video/BV15 ...
- X86架构CPU下Ubuntu系统环境源码编译pytorch-gpu-2.0.1版本
本文操作步骤与 aarch64架构CPU下Ubuntu系统环境源码编译pytorch-gpu-2.0.1版本大致相同,只是CPU架构不同而已,因此这里只记录不同的地方. 重点: 一个个人心得,那就是要 ...
- windows10开启电源模式中的休眠选项
使用管理员权限开启PowerShell,输入命令: powercfg -h on
- 【转载】 python鸭子类型与protocol
版权声明:本文为CSDN博主「yuanzhoulvpi」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/yuan ...
- springboot与redisson整合时读取配置文件为null
1.背景 在springboot整合redisson是读取配置文件为null 2.解决方案 这两个jar包可能存在冲突 <!-- redisson-spring-boot-starter --& ...
- zabbix 二次开发(添加menu)
zabbix 二次开发--- 在zabbix菜单栏中增加 CMDB 菜单,该菜单下有个子栏目 CMDB overview,如图: 实现此效果,我们需要修改两个地方:menu.inc.php 和 mai ...
- CDH添加es服务
地址:https://blog.csdn.net/guoliduo/article/details/105072857 注意:目前只支持cdh5.x的版本安装es,cdh6.x暂不支持.
- Linux 内核相关命令
Shell 命令: ipcs # 查看共享内存 dmesg # 显示内核消息 sudo dmesg -c # 清空内核消息 sudo mknod /dev/rwbuf c 60 0 sudo insm ...
- Honor X20 忽然不能与Android Studio 连接
背景:前一天还正常使用,可以连接Android Studio,第二天就连不上了 已知:数据线没问题,驱动没问题,设备开了开发者模式,连接上电脑时会提示已连接USB调试, 测试过程:(点击撤销USB调试 ...
- 分布式缓存应用场景与redis持久化机制
redis 参考目录: 生产级Redis 高并发分布式锁实战1:高并发分布式锁如何实现 https://www.cnblogs.com/yizhiamumu/p/16556153.html 生产级Re ...