Kotlin原理详析之拓展函数
作者:林嘉伟 发布时间:2023-04-10 19:54:08
原理
拓展函数是kotlin里一个比较常用的特性,例如我们可以给Context拓展一个toast方法:
// MainActivity.kt
fun Context.toast(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
}
private fun foo(context: Context) {
context.toast("hello world")
}
它的原理其实很简单,就是生成了一个toast方法。拓展函数的this指针实际上是这个生成方法的第一个参数:
/* compiled from: MainActivity.kt */
public final class MainActivityKt {
public static final void toast(Context $this$toast, String msg) {
//参数判空
...
// 拓展函数代码
Toast.makeText($this$toast, msg, 0).show();
}
}
所以这个this指针实际上是由函数调用的地方传入的对象引用:
private final void foo(Context context) {
MainActivityKt.toast(context, "hello world");
}
限制
知道了拓展函数的实现原理之后我们就能从原理去理解拓展函数的种种限制.
不能访问私有成员
由于编译成java之后,生成的拓展方法实际是靠第一个参数出入对象引用,然后使用这个对象引用去调用对象的方法。因此我们并没有权限在拓展函数里面调用私有方法:
class TestClass {
fun publicFun() {}
private fun privateFun() {}
}
fun TestClass.extFun() {
publicFun() // 正确,可以调用公有方法
privateFun() // 错误,不能调用私有方法
}
拓展函数不能实现多态
由于拓展函数并不是真的给类增加一个成员函数,所以父类和子类的同名拓展函数并没有多态的特性。
例如我们为父类和子类拓展同一个foo()函数:
open class Parent
class Child : Parent()
fun Parent.foo() {
println("parent")
}
fun Child.foo() {
println("child")
}
然后只要将子类转换成父类,调用的拓展函数就是父类的拓展函数:
val child = Child()
child.foo()
(child as Parent).foo()
// 输出:
// child
// parent
成员函数优先级高,拓展函数不能实现重写
当拓展函数与类本身或者父类的成员函数相同,在实际调用的时候会优先调用成员函数,并不会出现类似重写的效果.
例如我们为一个类编写了一个与成员函数相同的拓展函数,实际优先调用类成员函数:
open class Parent {
fun foo() {
println("foo")
}
}
fun Parent.foo() {
println("parent")
}
Parent().foo()
// 输出:
// foo
就算是为子类编写了一个与父类成员函数相同的拓展函数,也会优先调用父类的成员函数:
open class Parent {
fun foo() {
println("foo")
}
}
class Child : Parent()
fun Child.foo() {
println("child")
}
Child().foo()
// 输出:
// foo
关闭
为什么要使用Kotlin中的扩展函数
我们都知道在Koltin这门语言可以与Java有非常好的互操作性,所以扩展函数这个新特性可以很平滑与现有Java代码集成。甚至纯Kotlin的项目都可以基于Java库,甚至Android中的一些框架库,第三方库来构建。扩展函数非常适合Kotlin和Java语言混合开发模式。在很多公司一些比较稳定良好的库都是Java写,也完全没必要去用Kotlin语言重写。但是想要扩展库的接口和功能,这时候扩展函数可能就会派上用场。使用Kotlin的扩展函数还有一个好处就是没有副作用,不会对原有库代码或功能产生影响。先来看下扩展函数长啥样
给TextView设置加粗简单的例子
//扩展函数定义
fun TextView.isBold() = this.apply {
paint.isFakeBoldText = true
}
//扩展函数调用
activity.find<TextView>(R.id.course_comment_tv_score).isBold()
来源:https://blog.islinjw.cn/2022/01/25/Kotlin原理-拓展函数/


猜你喜欢
- 最近正好也没什么可忙的,就回过头来鼓捣过去的知识点,到Servlet部分时,以前学习的时候硬是把从上到下的继承关系和接口实现记得乱七八糟。这
- 今天看《第一行代码》上面关于拍照和从相册选取图片那一部分,发现始终出不来效果,所以搜索其他资料学习一下相关知识,写一个简单的Demo。&nb
- 今天,我们接着讲微信支付的系列教程,前面,我们讲了这个微信红包和扫码支付。现在,我们讲讲这个公众号支付。公众号支付的应用环境常见的用户通过公
- 前言Spring JPA是目前比较常用的ORM解决方案,但是其对于某些场景并不是特别的方便,例如查询部分字段,联表查询,子查询等。而接下来我
- 这篇文章说明了如何通过Maven配置Spring依赖项。最新的Spring版本可以在Maven Central上找到。Maven中的Spri
- 在上篇文章给大家介绍了FastDFS安装和配置整合Nginx-1.13.3的方法,大家可以点击查看下。今天使用Java代码实现文件的上传和下
- 引言对使用 lombok 还是有很多争议的,有些公司不建议使用,有些公司又大量使用。我们的想法是:可以使用,但是不要滥用。什么是 lombo
- 一个简单的实现版本,没有去Hook键鼠等操作,事先录制好操作步骤(将鼠标移动到需要操作的位置,按下热键执行相应动作),点击运行即可。主要还是
- Android11 读写权限申请Android11系统对应用写入权限做了严格的限制。本文介绍如何获取文件读写权限。项目中 build.gra
- Spring Security中也提供了默认的注销配置,在开发时也可以按照自己需求对注销进行个性化定制开启注销 默认开启package co
- 好了下面进入正题,我们先看一下实现效果吧:下面来介绍一下代码: 本思路就是:1.先到手机中扫描jpeg和png的图片2.获取导图片
- Spring Security简介Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的
- 首先我们发现现在我们所用的android智能手机大部分都有当你在打电话时按power键来挂断电话,一般都是在设置中。 我主要是在原生源码中添
- 前言:在实际的应用开发中,很多时候往往因为一些不可控的因素导致程序出现一些错误,这个时候就要及时把异常信息反馈给客户端,便于客户端能够及时地
- init和clinit区别①init和clinit方法执行时机不同init是对象构造器方法,也就是说在程序执行 new 一个对象调用该对象类
- 我就废话不多说了,大家还是直接看代码吧~/** * feign调用客户端 */@FeignClient(name = "user&
- 我们在使用一些开源调度系统(比如:elastic-job等)的时候,对于任务的执行时间通常都是有规律性的,可能是每隔半小时执行一次,或者每天
- 前言在日常的测试工作过程中,app可能会出现闪退崩溃的情况,这个时候就需要测试同学快速抓取到崩溃日志,来有效的辅助开发定位问题,快速的去解决
- Android开发之Android.mk模板的实例详解关于Android NDK开发的文章已经比较多了,我的博客中也分享了很多NDK开发相关
- 目录1 Semaphore的主要方法2 实例讲解实现单例模式3 源码解析构造方法获取许可释放许可减小许可数量获取剩余许可数量前言:Semap