Kotlin原理详析之拓展函数

目录
  • 原理
  • 限制
    • 不能访问私有成员
    • 拓展函数不能实现多态
    • 成员函数优先级高,拓展函数不能实现重写
  • 为什么要使用Kotlin中的扩展函数
  • 总结

原理

拓展函数是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()

总结

到此这篇关于Kotlin原理详析之拓展函数的文章就介绍到这了,更多相关Kotlin原理拓展函数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Kotlin中常见内联扩展函数的使用方法教程

    前言 Kotlin一个强大之处就在于它的扩展函数,巧妙的运用这些扩展函数可以让你写出的代码更加优雅,阅读起来更加流畅,下面总结了在开发中经常用到的一些内联扩展函数.经常有小伙伴搞不懂with,run,apply等等这些函数该怎么用,在哪里用,我的建议是先记住每个函数的功能(无非就是它需要什么参数?返回值是什么?)记住这两点再根据实际开发中的场景慢慢的就能熟练运用了.其实这些函数极其类似,不同的函数可以完成同样的功能,通过下面的实例也能看出.而在我以往的开发经验中这些函数主要的使用场景有两个,一是

  • Kotlin扩展函数及实现机制的深入探索

    前言 2017年Google IO大会宣布使用Kotlin作为Android的官方开发语言,相比较与典型的面相对象的JAVA语言,Kotlin作为一种新式的函数式编程语言,也有人称之为Android平台的Swift语言. 先让我们看下实现同样的功能,Java和Kotiln的对比: // JAVA,20多行代码,充斥着findViewById,类型转换,匿名内部类这样的无意义代码 public class MainJavaActivity extends Activity { @Override

  • Kotlin 扩展函数和扩展属性的使用方法

    Kotlin 能够扩展一个类的新功能而无需继承该类或者使用像装饰者这样的设计模式. 这通过叫做 扩展 的特殊声明完成. 例如,你可以为一个你不能修改的.来自第三方库中的类编写一个新的函数. 这个新增的函数就像那个原始类本来就有的函数一样,可以用普通的方法调用. 这种机制称为 扩展函数 .此外,也有 扩展属性 , 允许你为一个已经存在的类添加新的属性. 前言 作为安卓开发,我们常常碰到这样的场景,需要把以dp为单位的值转化为以px为单位.这时候我们常会写一个Utils类,比如说 public cl

  • Kotlin中的扩展函数与属性示例详解

    前言 Kotlin 中类的扩展方法并不是在原类的内部进行拓展,通过反编译为Java代码,可以发现,其原理是使用装饰模式,对源类实例的操作和包装,其实际相当于我们在 Java中定义的工具类方法,并且该工具类方法是使用调用者为第一个参数的,然后在工具方法中操作该调用者: 理论上来说,扩展函数很简单,它就是一个类的成员函数,不过定义在类的外面.让我们来添加一个方法,来计算一个字符串的最后一个字符: package strings /** * @author :Reginer in 2018/5/22

  • Kotlin原理详析之拓展函数

    目录 原理 限制 不能访问私有成员 拓展函数不能实现多态 成员函数优先级高,拓展函数不能实现重写 为什么要使用Kotlin中的扩展函数 总结 原理 拓展函数是kotlin里一个比较常用的特性,例如我们可以给Context拓展一个toast方法: // MainActivity.kt fun Context.toast(msg: String) {     Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() } private fun foo(

  • Spring Boot 中starter的原理详析

    目录 1.springboot 的starter 的启动原理是什么 原理 来个例子 小结 2.springboot 是如何找到配置类的 3.springboot starter 的bean 是怎么加载到容器的 4.总结 前言: 今天介绍springboot ,也是写下springboot的插件机制,starter的原理,其实这个网上已经很多了,也是看了不少别人的文章,今天主要还是带着问题去记录下. 1.springboot 的starter 的启动原理是什么 原理 这个问题是很简单的,只要了解s

  • Kotlin如何直接使用控件ID原理详析

    前言 最近断断续续地把项目的界面部分的代码由JAva改成了Kotlin编写,并且如果应用了kotlin-android-extensions插件,一个显而易见的好处是再也不用写 findViewById()来实例化你的控件对象了,直接操作你在布局文件里的id即可,这一点我感觉比butterknife做的还简洁友好. Activity import android.support.v7.app.AppCompatActivity import android.os.Bundle import ko

  • iOS中block变量捕获原理详析

    Block概述 Block它是C语言级别和运行时方面的一个特征.Block封装了一段代码逻辑,也用{}括起,和标准C语言中的函数/函数指针很相似,此外就是blokc能够对定义环境中的变量可以引用到.这一点和其它各种语言中所说的"闭包"是非常类似的概念.在iOS中,block有很多应用场景,比如对代码封装作为参数传递.这在使用dispatch并发(Operation中也有BlockOperation)和completion异步回调等处都广泛应用. Block是苹果官方特别推荐使用的数据类

  • Spring Cloud Ribbon的踩坑记录与原理详析

    简介 Spring Cloud Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的.它不像服务注册中心.配置中心.API网关那样独立部署,但是它几乎存在于每个微服务的基础设施中.包括前面的提供的声明式服务调用也是基于该Ribbon实现的.理解Ribbon对于我们使用Spring Cloud来讲非常的重要,因为负载均衡是对系统的高可用.网络压力的缓解和处理能力扩容的重要手段之一.在上节的例子中,我们采用了声明式的方式来实现负载均衡.实际上,内部

  • JAVA字符串类型switch的底层原理详析

    前言 switch 语句是非常的基础的知识,掌握起来也不难掌握,语法比较简单.但大部分人基本是知其然,不知其所以然.譬如 早期JDK只允许switch的表达式的值 int及int类型以下的基本类型,后期的JDK却允许匹配比较 字符串.枚举类型,这是怎么做到的呢?原理是什么?本文将深入去探索. 基础 我们现在使用的Java的版本,基本上是都支持String类型的.当然除了String类型,还有int.char.byte.short.enum等等也都是支持的.然而在其底部实现中,还是基于 整型的,也

  • Redis集群的离线安装步骤及原理详析

    前言 本文主要是记录一下Redis集群在linux系统下离线的安装步骤,毕竟在生产环境下一般都是无法联网的,Redis的集群的Ruby环境安装过程还是很麻烦的,涉及到很多的依赖的安装,所以写了一个文章来进行记录.本文分为两部分,第一部分先通过原生命令的安装来实现redis集群的部署,通过原生命令的安装对于了解redis集群的实现原理有很大的帮助,第二部分通过官方工具Ruby来进行Redis集群的安装,通过Ruby安装Redis集群的时候主要是搭建好Ruby环境,真正Redis集群的安装配置通过R

  • Flutter启动页(闪屏页)的具体实现及原理详析

    为什么要有启动页? 在以下文章中,启动页就是闪屏页. 现在大部分App都有启动页,那么为什么要有启动页?这是个值得思考的问题,如果没有启动页会怎样,大部分的App会白屏(也有可能是黑屏,主题设置有关系)非常短的时间,然后才能展示App的内容. 那么问题来了,一定要有启动页吗?答案:不是,而且是尽可能不要有启动页,因为启动页会让用户体验不够连贯,甚至IOS在开发手册上就不推荐使用启动页. 我们深入思考一下,既然不推荐为什么这样流行,答案非常简单,启动页的成本非常低,如果你想把的App启动优化到一个

  • Android Handler机制的工作原理详析

    写在前面 上一次写完Binder学习笔记之后,再去看一遍Activity的启动流程,因为了解了Binder的基本原理,这次看印象会更深一点,学习效果也比以前好很多.本来打算直接来写Activity的启动流程的,但总觉得Handler也需要写一下,知道Handler和Binder的原理后,再去看Activity的启动流程,应该也没什么问题了.虽然网上已经有很多Handler相关的文章了,而且Handler机制的上层原理也并不难,还是决定写一下,因为我想构建自己的知识体系.也希望给看我博客的朋友们一

  • 关于Android触摸事件分发的原理详析

    目录 一:前言 二:说在前面的知识 三:整体流程 1:activity 2:window就是PhoneWindow 3:view group 4:view 四:一些关键点 五:从源码看触摸事件分发 总结 一:前言 最近在学Android的触摸事件分发,我觉得网上说的太杂太乱,而且有很多博客都有明显的错误.什么自顶向下分发,自下向顶分发,什么拦截又一直消费什么什么之类,非常难懂.为了自己将来回顾可以更好的理解这块知识,也为了后来之人可以更好的学习,我写下这篇博客. 二:说在前面的知识 点击,滑动,

随机推荐