简述: 今天带来的是Kotlin浅谈系列的第五弹,这讲主要是讲利用Kotlin中的扩展函数特性让咱们的代码变得更加简单和整洁。扩展函数是Kotlin语言中独有的新特性,利用它能够减小不少的样板代码,大大提升开发的效率;此外扩展函数的使用也是很是简单的。我会从如下几个方面阐述Kotlin中的扩展函数。java
咱们都知道在Koltin这门语言能够与Java有很是好的互操做性,因此扩展函数这个新特性能够很平滑与现有Java代码集成。甚至纯Kotlin的项目均可以基于Java库,甚至Android中的一些框架库,第三方库来构建。扩展函数很是适合Kotlin和Java语言混合开发模式。在不少公司一些比较稳定良好的库都是Java写,也彻底不必去用Kotlin语言重写。可是想要扩展库的接口和功能,这时候扩展函数可能就会派上用场。使用Kotlin的扩展函数还有一个好处就是没有反作用,不会对原有库代码或功能产生影响。先来看下扩展函数长啥样app
//扩展函数定义
fun TextView.isBold() = this.apply {
paint.isFakeBoldText = true
}
//扩展函数调用
activity.find<TextView>(R.id.course_comment_tv_score).isBold()
复制代码
只须要把扩展的类或者接口名称,放到即将要添加的函数名前面。这个类或者名称就叫作接收者类型,类的名称与函数之间用"."调用链接。this指代的就是接收者对象,它能够访问扩展的这个类可访问的方法和属性。框架
注意: 接收者类型是由扩展函数定义的,而接收者对象正是这个接收者类型的对象实例,那么这个对象实例就能够访问这个类中成员方法和属性,因此通常会把扩展函数当作成员函数来用。ide
//扩展属性定义
var TextView.isBolder: Boolean
get() {//必须定义get()方法,由于不能在现有对象添加字段,也天然就没有了默认的get()实现
return this.paint.isFakeBoldText
}
set(value) {
this.paint.isFakeBoldText = value
}
//扩展属性调用
activity.find<TextView>(R.id.course_comment_tv_score).isBolder = true
复制代码
注意:函数
扩展属性和扩展函数定义相似,也有接收者类型和接收者对象,接收者对象也是接收者类型的一个实例,通常能够把它当作类中成员属性来使用。测试
必须定义get()方法,在Kotlin中类中的属性都是默认添加get()方法的,可是因为扩展属性并非给现有库中的类添加额外的属性,天然就没有默认get()方法实现之说。因此必须手动添加get()方法。ui
因为重写了set()方法,说明这个属性访问权限是可读和可写,须要使用varthis
咱们从上面例子能够看出,kotlin的扩展函数真是强大,能够毫无反作用给原有库的类增长属性和方法,好比例子中TextView,咱们根本没有去动TextView源码,可是却给它增长一个扩展属性和函数。具备那么强大功能,到底它背后原理是什么?其实很简单,经过decompile看下反编译后对应的Java代码就一目了然了。spa
扩展函数实际上就是一个对应Java中的静态函数,这个静态函数参数为接收者类型的对象,而后利用这个对象就能够访问这个类中的成员属性和方法了,而且最后返回一个这个接收者类型对象自己。这样在外部感受和使用类的成员函数是同样的。翻译
public final class ExtendsionTextViewKt {//这个类名就是顶层文件名+“Kt”后缀,这个知识上篇博客有详细介绍
@NotNull
public static final TextView isBold(@NotNull TextView $receiver) {//扩展函数isBold对应其实是Java中的静态函数,而且传入一个接收者类型对象做为参数
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
$receiver.getPaint().setFakeBoldText(true);//设置加粗
return $receiver;//最后返回这个接收者对象自身,以至于咱们在Kotlin中彻底可使用this替代接收者对象或者直接不写。
}
}
复制代码
分析完Kotlin中扩展函数的原理,咱们也就很清楚,如何在Java中去调用Kotlin中定义好的扩展函数了,实际上使用方法就是静态函数调用,和咱们以前讲的顶层函数在Java中调用相似,不过惟一不一样是须要传入一个接收者对象参数。
ExtendsionTextViewKt.isBold(activity.findViewById(R.id.course_comment_tv_score));//直接调用静态函数
复制代码
扩展属性实际上就是提供某个属性访问的set,get方法,这两个set,get方法是静态函数,同时都会传入一个接收者类型的对象,而后在其内部用这个对象实例去访问和修改对象所对应的类的属性。
public final class ExtendsionTextViewKt {
//get()方法所对应生成静态函数,而且传入一个接收者类型对象做为参数
public static final boolean isBolder(@NotNull TextView $receiver) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return $receiver.getPaint().isFakeBoldText();
}
//set()方法所对应生成静态函数,而且传入一个接收者类型对象做为参数和一个须要set的参数
public static final void setBolder(@NotNull TextView $receiver, boolean value) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
$receiver.getPaint().setFakeBoldText(true);
}
}
复制代码
Java调用Kotlin中定义的扩展属性也很简单,就至关于直接调用生成的set(),get()方法同样。
ExtendsionTextViewKt.setBolder(activity.findViewById(R.id.course_comment_tv_score), true);
复制代码
说到扩展函数和成员函数的区别,经过上面例子咱们已经很清楚了,这里作个概括总结:
在Kotlin和Java中咱们都知道类的成员函数是能够被重写的,子类是能够重写父类的成员函数,可是子类是不能够重写父类的扩展函数。
open class Animal {
open fun shout() = println("animal is shout")//定义成员函数
}
class Cat: Animal() {
override fun shout() {
println("Cat is shout")//子类重写父类成员函数
}
}
//定义子类和父类扩展函数
fun Animal.eat() = println("Animal eat something")
fun Cat.eat()= println("Cat eat fish")
//测试
fun main(args: Array<String>) {
val animal: Animal = Cat()
println("成员函数测试: ${animal.shout()}")
println("扩展函数测试: ${animal.eat()}")
}
复制代码
运行结果:
以上运行结果再次说明了扩展函数并非类的一部分,它是声明与类外部的,尽管子类和父类拥有了相同的扩展函数,可是实际上扩展函数是静态函数。从编译内部来看,子类和父类拥有了相同的扩展函数,实际上就是定义两个同名的静态扩展函数分别传入父类对象和子类对象,那么调用的方法确定也是父类中的方法和子类中的方法,因此输出确定是父类的。
欢迎关注Kotlin开发者联盟,这里有最新Kotlin技术文章,每周会不按期翻译一篇Kotlin国外技术文章。若是你也喜欢Kotlin,欢迎加入咱们~~~