首页 > 其他分享 >kotlin的拓展函数和原理

kotlin的拓展函数和原理

时间:2023-02-03 11:05:46浏览次数:39  
标签:run 函数 kotlin Dog 拓展 Animal fun

kotlin的拓展函数和原理

问题背景

kotlin的使用过程中有个拓展函数的概念,这个概念在java中是没有的,那么问题来了,kotlin中拓展函数是什么呢? 拓展函数的概念:不改变原有类的情况下,增加新的方法,扩展新的功能。下面一起看下具体的使用和原理分析。

问题分析

(1)kotlin中使用拓展函数

创建一个普通的类DogKt,类里面有两个已经存在的方法,run()和cry()。

class Dog{
    fun run() = "狗在跑"
    fun eat() = "狗在吃东西"
}

狗狗本身就有跑和吃两个技能,而现在需要增加叫的技能,那就用扩展函数来进行扩展。在需要被扩展的类的后面,添加一个方法即可,如下: fun DogKt.order() = "扩展功能-》狗听从指令" 创建好拓展函数后,调用如下所示:

fun main() {
    val dog = Dog()
    println(dog.run())
    println(dog.eat())
    // 调用dog的拓展函数
    println(dog.cry())
}

class Dog{
    fun run() = "狗在跑"
    fun eat() = "狗在吃东西"
}

fun Dog.cry() = "狗在叫"

运行结果如下: image.png

(2)拓展函数原理分析

将上面的kotlin代码反编译成java代码如下(具体反编译方法可参考 https://blog.51cto.com/baorant24/6034450 (2)中介绍):

// TestKt.java
...
public final class TestKt {
   public static final void main() {
      Dog dog = new Dog();
      String var1 = dog.run();
      System.out.println(var1);
      var1 = dog.eat();
      System.out.println(var1);
      var1 = cry(dog);
      System.out.println(var1);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }

    // 拓展函数对应的代码
   @NotNull
   public static final String cry(@NotNull Dog $this$cry) {
      Intrinsics.checkNotNullParameter($this$cry, "$this$cry");
      return "狗在叫";
   }
}
// Dog.java
...
public final class Dog {
   @NotNull
   public final String run() {
      return "狗在跑";
   }

   @NotNull
   public final String eat() {
      return "狗在吃东西";
   }
}

拓展函数的原理:由反编译的java代码很容易看出,kotlin的拓展函数并没有改变对应类本身的结构,也就是说拓展的类本身并没有真的增加方法。而是增加了一个方法,将拓展的类对象作为方法的第一个参数传入,然后进行对应的调用。

(3)拓展函数的限制分析

了解了拓展函数的原理之后,我们来分析下类拓展函数的部分限制。 不能访问私有成员 由于编译成java之后,生成的拓展方法实际是靠第一个参数出入对象引用,然后使用这个对象引用去调用对象的方法。因此我们并没有权限在拓展函数里面调用私有方法:

class TestExt {
    fun publicFun() {}
    private fun privateFun() {}
}

fun TestExt.extFun() {
    publicFun() // 正确,可以调用公有方法

    privateFun() // 错误,不能调用私有方法
}

扩展函数不支持多态 我们可以先看看Java中的多态:这里有一个父类Aninal,里面存在一个run()方法,一个子类Dog继承Animal,类里面同样有一个run()方法,另外有一个调用类Person,存在一个call(Animal animal)。

//父类
class Animal {
    public String run() {
        return "Animal run";
    }
}

//子类
class Dog extends Animal {
    public String run() {
        return "Dog run";
    }
}

//第三个调用类
public class Person {
    public String call(Animal animal1) {
        return animal1.run();
    }

    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person.call(new Animal()));
        System.out.println(person.call(new Dog()));
    }
}

这个时候通过Person类分别来传入Animal和Dog的实例,都调用run()方法,可以得出以下结果: image.png 可以看出,在Java中,具体调用某一个方法,不是取决于所声明的类,而是取决于所引用的实例对象,比如上面例子中,call()方法其实声明的是Animal类,但是实际上如果传入的是Dog实例,那么最后也就得出Dog类的结果。 而在Kotlin的扩展函数中却是反过来的,扩展函数不支持多态,调用也只取决于对象的声明类型。 将上面例子中的类用Kotlin写一遍,代码如下:

open class Animal

class Dog : Animal()

//扩展函数
fun Animal.run() ="Animal run"
//扩展函数
fun Dog.run() = "Dog run"

fun person(animal: Animal) {
    println(animal.run())
}

fun main() {
    person(Animal())
    person(Dog())
}

运行结果如下: image.png 由运行结果可以看出,方法声明的是父类,调用的拓展函数就是父类的拓展函数,不会调用具体子类对象的拓展函数。 成员函数优先级高,拓展函数不能实现重写 当拓展函数与类本身或者父类的成员函数相同,在实际调用的时候会优先调用成员函数,并不会出现类似重写的效果. 例如我们为一个类编写了一个与成员函数相同的拓展函数,实际优先调用类成员函数,代码如下:

fun main() {
    Parent().foo()
}

open class Parent {
    fun foo() {
        println("foo")
    }
}

fun Parent.foo() {
    println("parent")
}

运行结果如下: image.png

问题总结

本文主要介绍了kotlin中类拓展函数的概念和原理,同时对拓展函数的部分限制做了说明,有兴趣的同学可以进一步深入研究。

标签:run,函数,kotlin,Dog,拓展,Animal,fun
From: https://blog.51cto.com/baorant24/6035572

相关文章

  • inline内联函数详解
    这几天看题解一直遇到inline所以学习总结一下1、C++inline内联函数(1)引入inline关键字的原因:在c/c++中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入......
  • c分配内存底层函数 realloc
    realloc(void*__ptr,size_t__size):更改已经配置的内存空间,即更改由malloc()函数分配的内存空间的大小。如果将分配的内存减少,realloc仅仅是改变索引的信息。如果是将......
  • oracle 自定义函数splitstr
     函数主体createorreplacetypetype_splitastableofvarchar2(4000) createorreplacefunctionsplitstr(p_stringvarchar2,......
  • #yyds干货盘点# 歌谣学前端之箭头函数1
    前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从......
  • PHP精度计算函数
    bcadd—将两个高精度数字相加bccomp—比较两个高精度数字,返回-1,0,1bcdiv—将两个高精度数字相除bcmod—求高精度数字余数bcmul—将两个高精度数字相乘bc......
  • TypeScript笔记 - 函数
    注解函数函数需要注解的部分其实只有参数值和返回值letcheckFunc=(str:string):boolean=>{returnstr['includes']('a')}checkFunc('abc') 使用接口......
  • py10函数之嵌套-名称空间作用域
    #函数是第一类对象:函数名指向的值可以被当中参数传递#1.函数名可以被传递#name='jason'#x=name#print(x)#print(id(x))#deffunc():#print('fromfunc')......
  • py09函数简介
     函数的返回值#deffunc():#return'asfjsfda'#res=func()#print(res)#函数内要想返回给调用者值必须用关键字return"""不写return只写return写returnNone......
  • Excel函数学习
    1.单条件求和=SUMIF(条件区域,求和条件,实际求和区域)2.多条件求和=SUMIFS(需要求和区域,条件区域1,求和条件1,条件区域2,求和条件2,......)3.单元格所在行位置=ROW(单......
  • C++ 虚函数底层表达
    转载一篇乐哥的博客,对虚函数,虚函数表和派生类对象赋予给基类指针时地址的变化等会有更深的理解https://mp.weixin.qq.com/s?__biz=Mzk0MzI4OTI1Ng==&mid=2247489554&idx=1......