KotLin扩展

举报
yd_221104950 发表于 2020/12/02 23:32:59 2020/12/02
【摘要】 Kotlin扩展 Kotlin提供了用一个新的函数来扩展一个类的能力,而不需要继承这个类或用设计模式如装饰者来实现。有了这个能力,我们就可以轻松为那些我们不能改变的第三方包中的类添加功能。为类添加了的函数,可以像类的其他函数一样调用。除了扩展函数外,还可以扩展属性,让你为已存在的类定义新属性。这个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

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。