【Kotlin 初学者】扩展-享受编程

简介: 一、介绍 Kotlin 可以对一个类的属性和函数进行扩展,且不需要继承或使用 Decorator 模式。 扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。

作者简介:CSDN博客专家、华为云·云享专家认证

系列专栏:Kotlin 初学者

学习交流:三人行必有我师焉;择其善者而从之,其不善者而改之。



一、介绍


     Kotlin 可以对一个类的属性和函数进行扩展,且不需要继承或使用 Decorator 模式。


       扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。


二、扩展函数


       扩展函数可以在已有类中添加新的函数,不会对原类做修改。


2.1 定义扩展函数


       扩展可以用于自定义类,也可以用于比如List、String,以及Kotlin标准库里的其他类。和继承相似,扩展也能共享类行为,在你无法接触某个类定义,或者某个类没有使用open修饰符,导致你无法继承它时,扩展就是增加类功能的最好选择。


//定义School类的扩展函数
fun School.student():String{
    return "$name-学习知识"
}


  • School:表示函数的接收者,也就是函数扩展的对象
  • student:扩展函数的名称
  • String:返回类型("$name-学习知识")

       其他的就和定义一般函数类似了。


2.1.1 定义School类


class School(var name:String){
    //自带函数
    fun teacher():String{
        return "$name-教书育人"
    }
}


2.1.2 扩展函数


//定义School类的扩展函数
fun School.student():String{
    return "$name-学习知识"
}
//定义String类的扩展函数
fun String.addSC():String{
    return "$this-帅次"
}
fun main() {
    //School类原来的函数
    println(School("超神学院").teacher())//超神学院-教书育人
    //School类的扩展函数
    println(School("超神学院").student())//超神学院-学习知识
    //String类的扩展函数
    println("Kotlin".addSC())//Kotlin-帅次
}


微信图片_20220525003248.png


2.1.3 扩展函数支持范围


       扩展函数定义后在同一个包下可直接调用,在其他包下则需要导入定义的扩展函数。在extension包下定义的扩展函数,在classkotlin包中使用。

       同一包下不能定义同名扩展函数。


import com.scc.kotlin.primary.extension.School
import com.scc.kotlin.primary.extension.addSC
import com.scc.kotlin.primary.extension.student
fun main() {
    println(School("Class包下").student())//Class包下-学习知识
    println("ClassKotlin".addSC())//ClassKotlin-帅次
}

微信图片_20220525003329.png


 在多个包下定义扩展函数addSC(),调用时:


微信图片_20220525003354.png


2.1.4 定义超类(Any)扩展函数


       因为Any是超类,所以它的扩展函数其他类也能直接使用。这个功能是真的强大。


//定义Any扩展函数
fun Any.eat(): Any {
    return this
}
fun main() {
    //因为Any是超类,所以它的扩展函数其他类也能直接使用
    println("超类".eat())//超类
    println(15.eat())//15
}


"超类".eat().addSC() 报错


    //报错,因为addSC()函数是String类的扩展函数,Any类无法直接调用
    //除非将 "超类".eat()的返回类型Any转为String再调用。
    "超类".eat().addSC()


这个时候我们该引入泛型扩展函数了。


2.1.5 泛型扩展函数


       新的泛型扩展函数不仅可以支持任何类型的接收者,还保留了接收者的类型信息,使用泛型类型后,扩展函数能够支持更多类型的接收者,适用范围更广了。


//定义泛型扩展函数
fun <T> T.scww():T{
    return this
}
fun main() {
    //WaKa调用泛型扩展函数scww(),返回String类型调用String类型扩展函数addSC()
    println("WaKa".scww().addSC())//WaKa-帅次
    //Int类型15调用泛型扩展函数scww(),返回Int类型调用apply{}函数
    println(15.scww().apply { })//15
}


  • String类型WaKa调用泛型扩展函数scww(),返回String类型调用String类型扩展函数addSC()


  • Int类型15调用泛型扩展函数scww(),返回Int类型调用apply{}函数


       泛型扩展函数在Kotlin标准库里随处可见,例如apply函数,apply函数被定义成了泛型扩展函数,所以能支持任何类型。


public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}


三、扩展属性


       除了给类添加功能扩展函数外,你还可以给类定义扩展属性。


       扩展属性允许定义在类或者kotlin文件中,不允许定义在函数中。初始化属性因为属性没有后端字段(backing field),所以不允许被初始化,只能由显式提供的 getter/setter 定义。


       扩展属性只能被声明为 val。


//定义扩展属性
//给String类添加一个扩展属性,这个扩展属性可以统计字符长度并加10。
val String.lengthSc
    get() = this.length.plus(10)
//给School类添加个position属性
val School.position
    get() = "${name}-高级中学"
fun main() {    
    println("一二三四五,上山打老虎".lengthSc)//21
    println(School("新冠中学").position)//新冠中学-高级中学
}


四、可控类扩展


       你也可以定义扩展函数用于可空类型,在可空类型上定义扩展函数,你就可以直接在扩展函数体内解决可能出现的空值问题。


//可空类扩展
//当String为null时打印四大皆空-default
fun String?.nullWithDefault(default: String) {
    println(this ?: "四大皆空-$default")
}
//当School为null时,打印default-学校没了
fun School?.dropOut(default: String) {
    println(this ?: "$default-学校没了")
}
fun main() {
    var str: String? = null
    str.nullWithDefault("空空")//四大皆空-空空
    str = "数据已到"
    str.nullWithDefault("空空")//数据已到
    var school: School? = null
    school.dropOut("没得名字")//没得名字-学校没了
    school = School("帅次学院")
    school.dropOut("默认退学")//com.scc.kotlin.primary.extension.School@19dfb72a
}


infix 关键字


       infix关键字适用于有单个参数的扩展和类函数,可以让你以更简洁的语法调用函数,如果一个函数定义使用了infix关键字,那么调用它时,接收者和函数之间的点操作以及参数的一对括号都可以不要。 (类似 to 函数)


//infix关键字
infix fun String?.nullInfixDefault(default: String){
    println(this ?: "Infix-$default")
}
fun main() {
    str = null
    str nullInfixDefault "空值"   //Infix-空值
    str = "不空"
    str nullInfixDefault "空值"  //不空
    //这个就类似mapOf
    mapOf("Key" to "Value")
}


to函数源码


       泛型拓展函数、前面加了infix关键字


public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)


  Kotlin标准库中的扩展函数:Kotlin标准库提供的很多功能都是通过扩展函数和扩展属性来实现的,包含类扩展的标准库文件通常都是以类名加s后缀来命名的,例如Sequences.kt,Ranges.kt,Maps.kt。


五、定义扩展文件


       扩展函数需要在多个文件里面使用,可以将它定义在单独的文件,然后import。


5.1 定义扩展文件


package com.scc.kotlin.primary.extension
//定义扩展文件
fun <T> T.scFile(): T {
    println("来自ScFile的问候")
    return this
}


5.2 使用


       同一包名下的文件调用scFile():


fun main() {
    str.scFile()//来自ScFile的问候
}


其他包下的文件调用scFile():


import com.scc.kotlin.primary.extension.scFile
fun main() {
    var userInfo4 = UserInfo4()
    userInfo4.scFile()//来自ScFile的问候
}


5.3 重命名扩展(import文件别名)


import com.scc.kotlin.primary.extension.scFile as scAliasFile
fun main() {
    var userInfo4 = UserInfo4()
    userInfo4.scAliasFile()//使用新定义的别名,来自ScFile的问候
//    userInfo4.scFile()//无法使用,报错
}


六、()->Unit 和 引用类型.()->Unit


package com.scc.kotlin.primary.extension
//() -> Unit:普通匿名函数
fun String.doAnonymous(anonymous:() -> Unit):String{
    anonymous()
    return "$this,AnonymousN"
}
//String.() -> Unit:匿名函数内部this指向一个String对象,隐式调用
fun String.doAnonymousString(anonymous: String.() -> Unit) :String{
    println(this)
    anonymous()
    return this
}
//这里使用的泛型匿名函数,使用其内部this
fun <T> T.doAnonymousT(anonymous: T.() -> Unit):T {
    println(this)
    anonymous()
    return this
}
fun main() {
    //普通匿名函数
    "a".doAnonymous{
//        println("$this,看看")//此处使用this,报错
    }
    //匿名函数内部this指向一个String对象
    "b".doAnonymousString{
        println("$this,看看")
    }
    15.doAnonymousT {
        println("$this,无敌是多么寂寞")
    }
}

微信图片_20220525005046.png


DSL


  使用这样的编程范式,就可以写出业界知名的“领域特定语言”(DSL),一种API编程范式,暴露接收者的函数和特性,以便于使用你定义的lambda表达式来读取和配置它们。

相关文章
|
7月前
|
Java 调度 Android开发
构建高效Android应用:探究Kotlin多线程编程
【2月更文挑战第17天】 在现代移动开发领域,性能优化一直是开发者关注的焦点。特别是在Android平台上,合理利用多线程技术可以显著提升应用程序的响应性和用户体验。本文将深入探讨使用Kotlin进行Android多线程编程的策略与实践,旨在为开发者提供系统化的解决方案和性能提升技巧。我们将从基础概念入手,逐步介绍高级特性,并通过实际案例分析如何有效利用Kotlin协程、线程池以及异步任务处理机制来构建一个更加高效的Android应用。
|
1月前
|
Java Kotlin Python
Kotlin - 扩展成员
Kotlin - 扩展成员
25 2
Kotlin - 扩展成员
|
1月前
|
Java Kotlin Python
Kotlin - 扩展成员
Kotlin - 扩展成员
28 2
Kotlin - 扩展成员
|
23天前
|
Java Kotlin Python
​ Kotlin教程笔记(16) - 扩展成员
​ Kotlin教程笔记(16) - 扩展成员
|
1月前
|
Java Kotlin Python
​ Kotlin教程笔记(16) - 扩展成员
​ Kotlin教程笔记(16) - 扩展成员
​ Kotlin教程笔记(16) - 扩展成员
|
2月前
|
Java Kotlin Python
Kotlin教程笔记(16) - 扩展成员
Kotlin教程笔记(16) - 扩展成员
25 2
Kotlin教程笔记(16) - 扩展成员
|
2月前
|
Java Kotlin Python
​ Kotlin教程笔记(16) - 扩展成员
​ Kotlin教程笔记(16) - 扩展成员
29 1
​ Kotlin教程笔记(16) - 扩展成员
|
1月前
|
Java Kotlin Python
​ Kotlin教程笔记(16) - 扩展成员
​ Kotlin教程笔记(16) - 扩展成员
28 0
​ Kotlin教程笔记(16) - 扩展成员
|
1月前
|
Java Kotlin Python
Kotlin教程笔记(16) - 扩展成员
Kotlin教程笔记(16) - 扩展成员
40 0
Kotlin教程笔记(16) - 扩展成员
|
2月前
|
Java Kotlin Python
Kotlin16 - 扩展成员
Kotlin16 - 扩展成员
Kotlin16 - 扩展成员