KotLin扩展
Kotlin扩展
Kotlin提供了用一个新的函数来扩展一个类的能力,而不需要继承这个类或用设计模式如装饰者来实现。有了这个能力,我们就可以轻松为那些我们不能改变的第三方包中的类添加功能。为类添加了的函数,可以像类的其他函数一样调用。除了扩展函数外,还可以扩展属性,让你为已存在的类定义新属性。这个Kotlin机制叫扩展函数。
扩展函数
声明一个扩展函数,我们需要在它的名字前加上接收器类型(即类名),即被扩展的类型。
// 定义一个类
class D{...}
// 为D类添加一个扩展函数
private fun D.hello(): String { return "Hello"
}
// 调用扩展函数
var d:D = D()
Log.i("Man",d.hello())
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
扩展一个类库中的类MutableList,为其添加一个swap函数:
// 扩展
fun<T> MutableList<T>.swap(index1: Int,index2: Int){ // 注意this,是指向MutableList对象的 var temp = this[index1] this[index1] = this[index2] this[index2] = temp
}
// 调用 val list = mutableListOf(1,2,3) list.swap(0,1)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
扩展函数调用的问题
open class Shape // 定义Shape类
class Rectangle: Shape() // 类Rectangle继承Shape类
fun Shape.getName() = "Shape" // Shape扩展了getName函数
fun Rectangle.getName() = "Rectangle" // Rectangle也扩展了一个getName函数
fun printClassName(s: Shape) {
println(s.getName())
}
printClassName(Rectangle()) // 结果为:Shape
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
因为被调用的扩展函数只依赖于声明的参数类型,此外因为是Shape,所以就会调用Shape的getName函数。
如果扩展的函数名与接收器类型(类)的成员同名,那么类的成员总是会胜出,即执行成员函数:
class Example {
fun printFunctionType() { println("Class method") }
}
fun Example.printFunctionType() { println("Extension function") }
Example().printFunctionType() // 结果总是:Class method
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
可空接收器
扩展也可以用可空类型来定义。这种扩展可以通过一个对象变量来调用,即使它的值是null,并且可以在方法体中检查this是否是null。这个特性允许我们在Koltin中调用toString()
,不需要查是否是null,因为这个检查会发生在扩展函数中:
fun Any?.toString(): String {
if (this == null) return "null"
// after the null check, 'this' is autocast to a non-null type, so the toString()below
// resolves to the member function of the Any class
return toString()
}
- 1
- 2
- 3
- 4
- 5
- 6
举多一个例子:
class D{...} // 定义一个类
// 定义一个D对象可空的扩展函数
fun D?.toHi():String{ if(this == null) return "null" // 在里面检查D对象是否为空 return "Hello!"
}
// 调用
var d: D? = null
Log.i("Man",d.toHi()) // null
var dd: D = D()
Log.i("Man",dd.toHi()) // Hello!
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
扩展属性
与函数相似,Kotlin也支持属性扩展。如给List类加一个lastIndex属性:
val <T> List<T>.lastIndex: Int
get() = size - 1
- 1
- 2
注意:由于Kotlin的扩展实际上不并没有在类中插入新的成员,所以没有有效途径给一个扩展属性拥有返回字段(field),这也是为什么扩展属性不允许用初始化器的原因,如:
val House.number = 1 // 错误!扩展属性不能使用初始化器,因为它不是接收器的成员,没有返回字段field
- 1
扩展属性的行为只能通过提供getters
,setters
来显式定义。
伴生对象
如果一个类有一个定义了的伴生对象,那么你可以为这伴生对象定义扩展函数和属性。就像普通的伴生对象成员,它可以只使用类名作为限定符来调用:
class A{ // 伴生对象MyCompanion companion object MyCompanion{ fun hello(){ Log.i("Man","Hello") } }
} class D{ // 匿名伴生对象 companion object{ fun hello(){ Log.i("Man","Hello") } }
}
// 扩展伴生对象
fun D.Companion.morning(){ Log.i("Man","Good morning")
}
fun A.MyCompanion.afternoon(){ Log.i("Man","Good afternoon")
}
// 调用
D.morning()
A.afternoon()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
扩展范围
绝大多数,我们都在顶层(即直接在包名下)定义扩展:
package org.example.declarations
fun List<String>.getLongestString() { /*...*/}
- 1
- 2
- 3
如果在它声明的包外使用扩展,我们需要在调用的地方引入它:
package org.example.usage
import org.example.declarations.getLongestString
fun main() {
val list = listOf("red", "green", "blue")
list.getLongestString()
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
将扩展声明为成员
在一个类里面,你可以为另一个类声明扩展。在这种扩展里,有多个隐式接收器-对象成员可以访问而不需要限定符。这个声明扩展的类的实例称为调度接收器(dispatch receiver),扩展方法的接收方类型的实例称为扩展接收方。
class Host(val hostname: String) {
fun printHostname() { print(hostname) }
}
class Connection(val host: Host, val port: Int) {
fun printPort() { print(port) }
// 扩展Host类
fun Host.printConnectionString() {
printHostname() // 调用 Host.printHostname()
print(":")
printPort() // 调用 Connection.printPort()
}
fun connect() {
/*...*/
host.printConnectionString()// 调用扩展函数
}
}
fun main() {
Connection(Host("kotl.in"), 443).connect()
//Host("kotl.in").printConnectionString(443) // 错误!这个扩展函数在Connection外不可用
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
如果调度接收器的成员与扩展接收器之间存在名称冲突,扩展接收器优先:
class Connection {
fun Host.getConnectionString() {
toString()// 调用Host.toString()
this@Connection.toString() // 调用Connection.toString()
}
}
- 1
- 2
- 3
- 4
- 5
- 6
声明为成员的扩展可以被声明为open
并且可以在子类中被重写。这意味着关于调度接收器类型,这类函数的分发是虚拟的。但是关于扩展接收器类型是静态的:
// 基类
open class Base { }
// 派生类
class Derived : Base() { }
open class BaseCaller {
// 扩展Base类
open fun Base.printFunctionInfo() {
println("Base extension function in BaseCaller")
}
// 扩展Derived类
open fun Derived.printFunctionInfo() {
println("Derived extension function in BaseCaller")
}
// 调用Base的扩展函数
fun call(b: Base) {
b.printFunctionInfo()
}
}
// DerivedCaller类
class DerivedCaller: BaseCaller() {
// 重写Base的扩展函数printFunctionInfo()
override fun Base.printFunctionInfo() {
println("Base extension function in DerivedCaller")
}
// 重写Derived的扩展函数printFunctionInfo()
override fun Derived.printFunctionInfo() {
println("Derived extension function in DerivedCaller")
}
}
// 测试
fun main() {
BaseCaller().call(Base())// "Base extension function in BaseCaller"
DerivedCaller().call(Base()) // "Base extension function in DerivedCaller"
DerivedCaller().call(Derived()) // "Base extension function in DerivedCaller"
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
扩展的可见性
扩展使用与在同一作用域中声明的常规函数相同的其他实体可见性,如 :
- 声明在一个文件的顶层的扩展可以访问在同一文件中其他顶层的private声明。
- 如果一个扩展被声明在它的接收器类型外面,那么这个扩展不能访问接收器的private成员。
关键要辨别出扩展的作用域。
文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/weixin_40763897/article/details/107659566
- 点赞
- 收藏
- 关注作者
评论(0)