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原理-拓展函数/
猜你喜欢
- MVC三层架构我们在刚刚成为程序员的时候,就会被前辈们 “教育” 说系统的设计要遵循 MVC(Model-View-Controller)架
- java * 的方法总结AOP的拦截功能是由java中的 * 来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该
- foreach嵌套使用if标签对象取值问题最近做项目过程中,涉及到需要在 Mybatis 中 使用 foreach 进行循环读取传入的查询条
- 泛型中占位符T和?有什么区别?这是一个好问题,有的人可能弄不清楚,所以我们这里简单的演示一下,相信大家一定能弄清楚的!先上两段代码:publ
- Mybatis条件if test使用枚举值1.正确package com.weather.weatherexpert.common.util
- 如下所示:if(str.indexOf(",") >= 0) System.out.println(&
- springboot开启事务很简单,只需要一个注解@Transactional 就可以了。因为在springboot中已经默认对jpa、jd
- 作者:京东零售 张宾1.背景在后台开发中,会经常用到线程池技术,对于线程池核心参数的配置很大程度上依靠经验。然而,由于系统运行过程中存在的不
- 使用filter设置要排除的URL@WebFilter(urlPatterns = "/*")@Order(value
- 快速排序快速排序是一种比较高效的排序算法,采用“分而治之”的思想,通过多次比较和交换来实现排序,在一
- 一、问题重现1.配置文件spring: #DataSource数据源 datasource: &nbs
- MyBatis @MapKey的妙用背景在实际开发中,有一些场景需要我们返回主键或者唯一键为Key、Entity为Value的Map集合,如
- 前言 之前unity5.x在代码中写了debug.log..等等,打
- 本文使用SpringBoot结合Redis进行简单的token鉴权。1.简介刚刚换了公司,所以最近有些忙碌,所以一直没有什么产出,最近朋友问
- 在任何Java面试当中多线程和并发方面的问题都是必不可少的一部分。如果你想获得任何股票投资银行的前台资讯职位,那么你应该准备很多关于多线程的
- 引言前边两章说了点基础的,从这章开始,我们挖挖源码。看看RocketMQ是怎么工作的。首先呢,这个生产者就是送孩子去码头的家长,孩子们呢,就
- 本文实例讲述了Java二维数组简单定义与使用方法。分享给大家供大家参考,具体如下:Java的二维数组是先创建一个一维数组,然后该数组的元素再
- OverView今天在复习的时候,突然复习到我们的相机操作,但是对于相机操作,对于我来说比较复杂的是对于权限的操作。所有我们需要对我们的相机
- Java 字节码以二进制的形式存储在 .class 文件中,每一个 .class 文件包含一个 Java 类或接口。Javaassist 就
- 本文实例讲述了Java设计模式之模板方法模式。分享给大家供大家参考,具体如下:我们在生活中,很多事情都包含特定的一些步骤。如去银行办理业务,