前言:别让欲望泯灭了希望。一、概述 ??Kotlin 提供了扩展具有新功能的类的能力,而不必从类继承或使用设计模式(如Decorator),这是通过称为扩展的特殊声明来实现的。例如,你可以从无法修改的第三方库位类编写新函数。这些函可以按照通常的方式调用,就像它们是原始类的方法一样。这种机制称为扩展函数。还有一些扩展属性允许你为现有类定义新属性。
简单点来说,扩展能在不继承类或者实现接口的情况下,扩展该类的功能。扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。
二、扩展函数 2.1 扩展函数
扩展函数可以在已有类中添加新的方法,不会对原类做修改。扩展函数是基于类型定义的,并不依赖于具体对象,而是依附于具体的类型。
要声明一个扩展函数,我们需要在它的名字前面加上一个接收方类型,也就是被扩展的类型。扩展函数定义形式如下:
fun receiverType.functionName(params){
//TODO
}
- receiverType:??表示函数接收者,也就是函数扩展对象;
- functionName:?表示扩展函数的名称;
- params:????表示扩展函数的参数,可以为
null
。
data class User(var name: String, var age: Int, var sex: String) {
var info: Info? = null
}
在 ExtensionsActivity 类中定义一个扩展函数,为 User 添加一个扩展函数,并调用:
class ExtensionsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//调用扩展函数
val user = User("Android", 0, "none")
user.extFunction("Kotlin")
} //为User添加扩展函数
fun User.extFunction(name: String) {
println("扩展函数:User.extFunction() | name == $name")
}
}
可以看到,在 ExtensionsActivity 类中,为 User 定义了一个扩展函数
extFunction()
,那么可以在任何User实例中调用这个函数。打印数据如下:扩展函数:User.extFunction() | name == Kotlin
(2)下面为
MutableList
添加一个 swap
函数:fun MutableList.swap(index1: Int, index2: Int) {
val temp = this[index1]//this 表示接受者对象,即MutableList
this[index1] = this[index2]
this[index2] = temp
}
扩展函数中的
this
关键字是指接收者对象(点之前传递的对象,比如上面的 MutableList)。现在,我们可以在任何 MutableList
上面调用这样的函数:val list = mutableListOf(1, 2, 3, 4)
list.swap(0, 2)//this中的swap()将会保存list的值
println("扩展函数:$list")
打印数据如下:
扩展函数:[3, 2, 1, 4]
当然,这个函数对任何
MutableList
都有意义,我们可以使用泛型表示:fun MutableList.swap2(index1: Int, index2: Int) {
val temp = this[index1]//this 表示接受者对象,即MutableList
this[index1] = this[index2]
this[index2] = temp
}
我们在函数名之前声明泛型类型参数,方便它在接收方类型表达式中可用。
2.2 扩展函数是静态解析的
这里涉及到派发机制,有人会问,什么是派发机制?
(1)面向对象的语言一般有两种派发机制,静态派发和动态派发,静态派发比如方法重载,在编译时就确定调用那个方法;而动态派发更多体现在多肽上,调用的方法在运行时才能确认。
open class Animal {
open fun movingWay() {
println("Animal:way")
}
}class Tiger : Animal() {
override fun movingWay() {
println("Tiger:way")
}
}fun printWay(animal: Animal) {
animal.movingWay()
} //执行
var tiger = Tiger()
printWay(tiger)//打印为:Tiger:way
//调用printWay(animal: Animal)方法,接收参数为Animal,传入的是Tiger,Tiger为Animal的子类,多肽场景。
上面的结果是执行了类Tiger中的
movingWay()
方法。类Tiger继承自Animal 并重写了 movingWay()
方法,编译器编译的时候并不知道 printWay(animal: Animal)
中参数的类型具体是什么,只有在运行的时候才知道传入的是 Tiger() 对象,所以才会去调用 Tiger() 中的 movingWay()
方法,这就是动态派发。(2)扩展实际上并不修改它们所扩展的类。通过定义扩展,不必向类中插入新成员,而只需要使用新函数可通过点符号对这种类的变量调用。
扩展函数是静态分配的,也就是说,它们不是按接收类型的虚拟成员。这意味着在调用扩展函数时,具体被调用的是哪一个函数,由调用该函数的表达式的类型决定的,而不是动态类型决定的。例如:
class ExtensionsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//调用
printName(Student())//传入实现类实例
}open class Person//基类class Student : Person()//Student 继承自 Personfun Person.getName() = "Person"//扩展函数fun Student.getName() = "Student"//扩展函数fun printName(person: Person) {//调用的扩展函数只依赖于参数的类型决定,这就是静态派发
println("扩展函数是静态解析的:${person.getName()}")
}
}
可以看到最终调用的是
Person.getName()
,打印数据如下:扩展函数是静态解析的:Person
因为所调用的扩展函数
printName(person: Person)
只依赖于参数 person
的声明类型,也就是 Person
类。不管你传入的是类 Person 还是 Student 的实例,我都执行参数类型声明的Person
类中的方法,这就是静态派发。(3)如果一个类有一个成员函数,并且定义了一个具有相同接收类型,函数名称相同,给定参数相同的扩展函数,那么会优先是使用成员函数:
class People() {
fun functionType() {
println("People:成员函数")
}
} //扩展函数,与上面的成员函数同类型同名
fun People.functionType() {
println("People:扩展函数")
}
//调用
var people = People()
people.functionType()
打印数据如下:
People:成员函数
但是,扩展函数重载具有相同函数名称但是参数不同的成员函数是完全可以的:
class People() {
fun functionType() {
println("People:成员函数")
}
} //扩展函数,与上面的成员函数同类型同名,但是参数类型不同
fun People.functionType(type: Int) {
println("People:扩展函数")
}
//调用
var people = People()
people.functionType(0)//只有扩展函数带了参数,调用的是扩展函数
打印数据如下:
People:扩展函数
三、扩展属性 与扩展函数类似,Kotlin 支持扩展属性:
val List.lastIndex: Int //扩展属性
get() = size - 1 fun main() {
val list = mutableListOf(1, 2, 3, 4)
println("扩展属性:${list.lastIndex}")//调用拓展属性
}
打印数据如下:
扩展属性:3
注意:由于扩展函实际上并不向类中插入成员,因此没有有效的方法让扩展属性具有后备字段。所以初始化器不允许用于扩展函数,只能由显式地提供setter/getter来定义。
//var Student.type = 0 //报错,扩展属性不能初始化
四、
null
的扩展函数 扩展可以用 null
的接收器类型定义,这样的扩展可以在对象变量上调用,即使它的值是 null
,并且可以检查主体内的 this==null
。这就是 Kotlin 中调用 toString()
而不检查是否为 null
的原因,检查发生在扩展函数内部。就是说,在扩展函数内,可以通过
this
来判断接收者是否为 null
,这样即使接收者为 null
也可以调用扩展函数。fun Any?.toString(): String {
if (this == null) return "null"
//在空检查之后,`this`自动转为非null类型,下面的toString()解析为Any类的成员函数
return toString()
} //调用
val name = null
name.toString()
五、伴生对象的扩展 如果一个类定义了一个伴生对象,你也可以为这个伴生对象定义扩展函数和属性,伴生对象通过
类名.
的形式调用伴生对象,伴生对象声明的扩展函数通过类名限定符来调用:class Dragon {
companion object {//伴生对象
//TODO
}
}
//伴生对象的扩展函数
fun Dragon.Companion.printCompanion() {
println("伴生对象的扩展:扩展函数")
}
//伴生对象的扩展属性
val Dragon.Companion.type: Int
get() = 0 //调用
Dragon.printCompanion()
println("伴生函数的扩展:type == " + Dragon.type)
打印数据如下:
伴生对象的扩展:扩展函数
伴生函数的扩展:type == 0
六、作用域的扩展 6.1 声明包外使用扩展
通常扩展的函数和属性定义在顶级top-level包下:
package com.suming.kotlindemo.normalfun User.share() {
//TODO
}
要在声明包之外使用定义的扩展,通过
import
导入扩展的函数名进行使用:package com.suming.kotlindemo.blogimport com.suming.kotlindemo.normal.share //import导入所有名为 kotlindemo.normal.share 的扩展//在*blog包中调用*normal中的share()扩展函数
var user = User()
user.share()
6.2 注意可见性
扩展利用其它实体的可见性,就像在相同作用域中声明的常规函数一样,比如:
- 在文件顶层声明的扩展可以访问同一文件中的其他
private
顶层声明; - 如果扩展在其接受者之外声明,则此类扩展不能访问接受者的私有成员。
package com.suming.kotlindemo.blog//top-level声明
private var sign = "扩展函数访问顶层声明私有成员"
//顶层扩展
fun User.topLevelType() {
println("sign == $sign")
}//调用扩展函数
fun main() {
val user = User()
user.topLevelType()
}
可以看到,在同一文件中,顶层拓展可以访问顶层
private
声明。打印数据如下:sign == 扩展函数访问顶层声明私有成员
七、扩展声明为成员 7.1 普通声明
在一个类内部你可以声明另一个类的扩展。这样的扩展中,有多个隐式接收者对象,它们的成员不需要限定符就可以访问。其中扩展所在类的实例称为分发接受者,而扩展方法的接收器类型的实例称为扩展接收者。
//扩展接受者
class Host() {
fun inviteHost() {
println("扩展为成员:Host")
}
}
//分发接受者
class Minor(val host: Host) {
fun inviteMinor() {
println("扩展为成员:Minor")
}
//Host的扩展函数,有多个隐式接收者对象
fun Host.extensionInvite() {
inviteHost()//回调Host中成员函数inviteHost()
inviteMinor()//回调Minor中成员函数inviteMinor()
}fun printInvite() {
host.extensionInvite()//回调扩展函数
}
} //调用
Minor(Host()).printInvite()
//Host().printInvite() //错误,扩展函数在 Minor() 不可用
打印数据如下:
扩展接受者:Host
分发接受者:Minor
在类 Minor 内创建了类 Host 的扩展,所以类 Minor 为分发接受者,而类 Host 为扩展接受者。拓展函数
Host.extensionInvite()
有多个隐式接受者,既有回调 Host 中成员函数 inviteHost()
,也可以回调 Minor 中成员函数 inviteMinor()
。可以看到在扩展函数中,分发接受者和扩展接受者的成员函数都可以调用。7.2 名称冲突
如果分发接受者和扩展接受者的成员之间存在名称冲突,则以扩展接受者优先,如果要引用分发接受者的成员,则可以使用限定的
this@类名.
语法。 //扩展接受者
class Host() {
//与 Minor 的函数 invite() 同名
fun invite() {
println("扩展接受者:Host")
}
}
//分发接受者
class Minor(val host: Host) {
//与 Host 的函数 invite() 同名
fun invite() {
println("分发接受者:Minor")
}fun Host.extensionInvite() {
invite()//这调用的是 Host 的函数 invite(),以扩展接受者优先
this@Minor.invite() //可以通过 this@类名. 限定符调用 Minor 的函数 invite()
}fun printInvite() {
host.extensionInvite()//回调扩展函数
}
} //执行
Minor(Host()).printInvite()
打印数据如下:
扩展接受者:Host
分发接受者:Minor
7.3 扩展声明为
open
以成员的形式定义的扩展可以声明为
open
,并可以在子类中重写。这意味着在这些扩展函数的分发过程中,对于分发接受者是虚拟的,但是对于扩展接受者扔是静态的。open class Base {}class Child : Base() {}open class Person {
open fun Base.shareBase() {
println("Base 扩展函数在 Person")
}open fun Child.shareBase() {
println("Child 扩展函数在 Person")
}fun printShare(base: Base) {
base.shareBase()//回调扩展函数
}
} //继承自Person,并重写扩展函数
open class Student : Person() {
override fun Base.shareBase() {
println("Base 扩展函数在 Student")
}override fun Child.shareBase() {
println("Child 扩展函数在 Student")
}
} //执行
Person().printShare(Base())//打印:Base 扩展函数在 Person
Person().printShare(Child())//打印:Base 扩展函数在 Person
Student().printShare(Base())//分发接受者虚拟解析,打印:Base 扩展函数在 Student
Student().printShare(Child())//扩展接收者静态解析,打印:Base 扩展函数在 Student
上面知道扩展接收者静态解析,在调用扩展函数
printShare(base: Base)
时,由调用该函数的表达式的类型决定的,而不是动态类型决定的,所以 printShare(base: Base)
函数无论传递的参数是 Base()
还是 Child()
实例,只会调用类Base中的,而不会调用类Child中的。而分发接受者是虚拟解析的,类Student继承自类Person,并重写其中的函数,运行时会调用对应实例的函数,如果子类没有复写则调用父类实现。
打印数据如下:
Base 扩展函数在 Person
Base 扩展函数在 Person
Base 扩展函数在 Student
Base 扩展函数在 Student
至此,本文结束!
【Kotlin专题「十二」(扩展Extensions(扩展函数与属性))】源码地址:https://github.com/FollowExcellence/KotlinDemo-master
请尊重原创者版权,转载请标明出处:https://blog.csdn.net/m0_37796683/article/details/108011223 谢谢!
推荐阅读
- 快速上手 Kotlin 开发系列之函数与函数嵌套
- 加深学习|android属性动画(Kotlin)
- android|一个简单的Android圆形ProgressBar
- Kotlin专题「十一」(可见性修饰符(private、protected、internal、public))
- Kotlin专题「十」(接口与函数接口(Functional (SAM) interfaces))
- Kotlin专题「十三」(数据类(Data Classes))
- Kotlin专题「十四」(密封类(Sealed Classes))
- Android开发者快速上手Kotlin(三) 之 高阶函数和SAM转换
- kotlin数字与java数字的不同