美文网首页Android开发Android知识
Kotlin Extension — Declaring Ext

Kotlin Extension — Declaring Ext

作者: wusp | 来源:发表于2018-01-14 10:58 被阅读21次

    由于官网的例子很全面,因此这里直接套用Kotlin官网的例子

    open class D {
    }
    
    class D1 : D() {
    }
    
    open class C {
        open fun D.foo() {
            println("D.foo in C")
        }
    
        open fun D1.foo() {
            println("D1.foo in C")
        }
    
        fun caller(d: D) {
            d.foo()   // call the extension function
        }
    }
    
    class C1 : C() {
        override fun D.foo() {
            println("D.foo in C1")
        }
    
        override fun D1.foo() {
            println("D1.foo in C1")
        }
    }
    
    C().caller(D())   // prints "D.foo in C"
    C1().caller(D())  // prints "D.foo in C1" - dispatch receiver is resolved virtually
    C().caller(D1())  // prints "D.foo in C" - extension receiver is resolved statically
    

    例子很简单,无非就是Extension的声明和如何调用Extension方法(注意:这里调用Members' Extension 通过了实例方法的转接,可以跟Kotlin Extension - Method中提到的无法在.kt文件中从实例或者外界直接拿到Extension方法引用联系起来)
    这里Kotlin定义了两个重要的概念:

    dispatch receiver: 指的便是例子中执行caller() 方法的C或者C1实例对象
    extension receiver: 指的便是例子中caller() 方法定义中执行Extension方法foo() 的对象

    接下来看看三个例子给我们揭示的一些重要现象

    1. C().caller(D1())

    很明显,我们这里往caller()方法里面传的是extension receiver类型是D1,但是为何最后调用的是父类D的Extension方法呢?
    通过反编译生成的.class文件我们找到的原因所在

    //C.class
    ...
    public final void caller(com.maxtropy.viewtest.D);
        Code:
           0: aload_1
           1: ldc           #39                 // String d
           3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
           6: aload_0
           7: aload_1
           8: invokevirtual #41                 // Method foo:(Lcom/maxtropy/viewtest/D;)V
          11: return
    ...
    
    //.class where C().caller(D1()) was executed
    ...
          32: invokespecial #20                 // Method com/maxtropy/viewtest/C."<init>":()V
          35: new           #29                 // class com/maxtropy/viewtest/D1
          38: dup
          39: invokespecial #30                 // Method com/maxtropy/viewtest/D1."<init>":()V
          42: checkcast     #22                 // class com/maxtropy/viewtest/D
          45: invokevirtual #27                 // Method com/maxtropy/viewtest/C.caller:(Lcom/maxtropy/viewtest/D;)V
    ...
    

    在caller()的入参中,d的参数类型是D,因此在调用caller()时,入参实例的类型就已经被上转型成D了。

    2. C1().caller(D())

    程序执行的结果很明显的告诉我们这是动态选择的结果,执行的是C1中的Extension方法,而不是父类C中的Extension方法。
    哇!在 top-level Extension反编译的例子中很明显的看到Extension方法被编译成了静态方法,这里怎么又跟动态选择有关了呢???
    同样施以反编译的魔法:

    //C.class
    Compiled from "C.kt"
    public class com.maxtropy.viewtest.C {
      public void foo(com.maxtropy.viewtest.D);
        Code:
           0: aload_1
           1: ldc           #9                  // String $receiver
           3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
           6: ldc           #17                 // String D.foo in C
           8: astore_2
           9: getstatic     #23                 // Field java/lang/System.out:Ljava/io/PrintStream;
          12: aload_2
          13: invokevirtual #29                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
          16: return
    
      public void foo(com.maxtropy.viewtest.D1);
        Code:
           0: aload_1
           1: ldc           #9                  // String $receiver
           3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
           6: ldc           #35                 // String D1.foo in C
           8: astore_2
           9: getstatic     #23                 // Field java/lang/System.out:Ljava/io/PrintStream;
          12: aload_2
          13: invokevirtual #29                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
          16: return
    
      public final void caller(com.maxtropy.viewtest.D);
        Code:
           0: aload_1
           1: ldc           #39                 // String d
           3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
           6: aload_0
           7: aload_1
           8: invokevirtual #41                 // Method foo:(Lcom/maxtropy/viewtest/D;)V
          11: return
    
      public com.maxtropy.viewtest.C();
        Code:
           0: aload_0
           1: invokespecial #45                 // Method java/lang/Object."<init>":()V
           4: return
    }
    
    //C1.class
    Compiled from "C.kt"
    public final class com.maxtropy.viewtest.C1 extends com.maxtropy.viewtest.C {
      public void foo(com.maxtropy.viewtest.D);
        Code:
           0: aload_1
           1: ldc           #9                  // String $receiver
           3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
           6: ldc           #17                 // String D.foo in C1
           8: astore_2
           9: getstatic     #23                 // Field java/lang/System.out:Ljava/io/PrintStream;
          12: aload_2
          13: invokevirtual #29                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
          16: return
    
      public void foo(com.maxtropy.viewtest.D1);
        Code:
           0: aload_1
           1: ldc           #9                  // String $receiver
           3: invokestatic  #15                 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
           6: ldc           #35                 // String D1.foo in C1
           8: astore_2
           9: getstatic     #23                 // Field java/lang/System.out:Ljava/io/PrintStream;
          12: aload_2
          13: invokevirtual #29                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
          16: return
    
      public com.maxtropy.viewtest.C1();
        Code:
           0: aload_0
           1: invokespecial #40                 // Method com/maxtropy/viewtest/C."<init>":()V
           4: return
    }
    

    原来声明在Member中的Extension方法全部被编译成了Member所在类型的实例方法!
    这实例方法有什么用呢?(再次重复这些实例方法在写.kt时无法通过实例直接拿到引用,也就c.foo()是不能通过编译的)
    玄妙便在call()方法的第6、行, 第6行的aload_0指的便是this, 也即是caller()被执行时真正的实例对象dispatch receiver。正是this,让Extension方法具备了动态选择的能力。

    相关文章

      网友评论

        本文标题:Kotlin Extension — Declaring Ext

        本文链接:https://www.haomeiwen.com/subject/kzkkoxtx.html