前言
本文介绍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中声明
与我们分析的一致。
以上就是扩展属性和扩展函数的全部内容