Kotlin

Kotlin基础(5)-扩展属性和扩展函数

2019-11-01  本文已影响0人  取了个很好听的名字

前言

本文介绍Kotlin扩展函数与扩展属性的相关方法

扩展属性

首先看如下的一段代码:

  var<T> MutableList<T>.charValue:T
      get() {return  this[0]}
      set(value) {this[0]=value}

  val list = mutableListOf<Int>(1, 2, 3)
        val value=list.charValue
        println(value)

测试结果

11-01 15:27:02.861 4257-4257/? I/System.out: 1

形式: XXX.YYY:ZZZ
其中XXX成为传播者类型,YYY为扩展属性的名称,ZZZ为扩展属性的类型

扩展属性实际上就是提供某个属性访问的set,get方法,这两个set,get方法是静态函数,同时都会传入一个接收者类型的对象,然后在其内部用这个对象实例去访问和修改对象所对应的类的属性。在本例中就是提供了set和get方法,并将MutableList传入,这样就获取到了MutableList的实例,通过对该实例的操作实现修改或获取MutableList的属性

java字节码如下

  public final Object getCharValue(@NotNull List $receiver) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      return $receiver.get(0);
   }

   public final void setCharValue(@NotNull List $receiver, Object value) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      $receiver.set(0, value);
   }

需要注意的是如果扩展的属性与扩展类的属性冲突,则优先使用扩展类的属性

class A(){
         var x:Int=100
         set(value){
             field=value
         } get(){
            return field
        }
    }

    val A.x:Int
        get() = 110;

测试结果

11-01 17:27:07.129 10252-10252/com.zhqy.javademo I/System.out: 100

扩展函数

扩展函数与扩展属性在原理上相似,将传播者类型的实例出入静态方法中,通过对该对象进行操作来实现扩展类的效果,类似于装饰者模式增强类的功能。

代码如下:

  fun TextView.setDefaultTextColor(color:Int):TextView{
        this.setTextColor(resources.getColor(color))
        return this
    }

   tv=findViewById(R.id.tv)
        tv.setDefaultTextColor(R.color.red)

java字节码

 public final void setDefaultTextColor(@NotNull TextView $receiver, int color) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      $receiver.setTextColor($receiver.getResources().getColor(color));
   }

从字节码中可以看出,第一个参数传入了一个Textiew的实例,即调用该方法的TextView实例,通过对该对象设置字体颜色,实现了改变字体颜色的效果。但需要注意的是扩展属性和扩展方法只有在调用它时才会有效果,且作用效果只针对调用它的对象实例

DR和ER

什么事DR和ER呢?DR 指的是分发接收者(Dispatcher Receiver),ER指的是扩展接收者(Extention Receiver)。扩展接收者指的是哪个类型的对象调用该扩展方法,该对象就是扩展接收者,声明扩展函数和属性的类被称为分发接收者,那么两者的含义又是什么呢?

先看一个扩展接收者(ER)的例子

  open class A(){
       open var x:Int=100;
    }


    class A1():A(){
       
    }


    fun A.printX(){
        println("${this.x} in A")
    }

    fun A1.printX(){
        println("${this.x} in A1")
    }

    fun printx(a:A){
        a.printX()
    }

   var a=A1();
        println(a.x)
        printx(a);

代码中声明了一个类A和A1 其中A1继承自A,声明了A的扩展函数和A1的扩展函数,并声明了printx来调用扩展函数,在调用时声明了一个A1的实例a并调用printx来调用扩展函数。因为声明了A1的扩展函数printX,所以应该打印${this.x} in A1这行文字,那么测试结果是否和我们预期的一样呢?

11-01 16:38:32.521 8605-8605/com.zhqy.javademo I/System.out: 100 in A

测试结果与我们预期的不一致,他并没有调用A1的扩展方法,而是调用其父类的A的扩展方法,这是为什么呢?这是因为扩展接收者遵循静态调用的原则,接收者类型由声明时决定,即你声明时传入的是什么类型的对象,就调用该对象对应的扩展方法或属性,在代码中 printx(a:A)放传入的参数是A类型的对象,所以调用的是A类型的扩展方法。

现在看一下DR的例子

open class A(){

}

class A1():A(){

}

open class B(){
    open fun A.foo(){
        println("A的扩展方法,在B中声明")
    }

    open fun A1.foo(){
        println("A1的扩展方法,在B中声明")
    }

    open fun call(a:A){
        a.foo()
    }
}

open class B1():B(){
    override fun A.foo(){
        println("A的扩展方法,在B1中声明")
    }

    override fun A1.foo(){
        println("A1的扩展方法,在B1中声明")
    }

    override fun call(a:A){
        a.foo()
    }
}


  var a=A();
         var a1=A1();
         var b=B();
         var b1=B1();

        b.call(a)
        b.call(a1)
        b1.call(a)
        b.call(a1);

在B和B1中有调用A或A1的扩展函数的方法call(A a),以及声明的A和A1的扩展函数,由于扩展接收者遵循类型由声明时决定,而call中传入的参数声明的是A类型,故执行A的扩展方法而不会执行其子类声明的扩展方法。那么会调用B还是B1关于A的扩展方法呢?这里引出分发接收者的定义,分发接收者指的是如果类中定义了其他类的扩展方法,那么这个类成为分发接收者,分发接收者遵循动态调用的原则,即接收者类型由运行时决定,即call方法在那个类中就执行哪个类中针对A的扩展方法。
测试结果如下:

11-01 17:17:28.445 9273-9273/com.zhqy.javademo I/System.out: A的扩展方法,在B中声明
11-01 17:17:28.446 9273-9273/com.zhqy.javademo I/System.out: A的扩展方法,在B中声明
11-01 17:17:28.446 9273-9273/com.zhqy.javademo I/System.out: A的扩展方法,在B1中声明
11-01 17:17:28.446 9273-9273/com.zhqy.javademo I/System.out: A的扩展方法,在B中声明

与我们分析的一致。

以上就是扩展属性和扩展函数的全部内容

上一篇 下一篇

猜你喜欢

热点阅读