【Kotlin】扩展函数 ( 扩展函数声明 | 调用方式 | 与成员函数优先级 | 接收者空值处理 )

2023-03-27 19:09:47 浏览数 (1)

文章目录
  • I . 扩展函数 总结
  • II . 扩展函数概念简介
  • III . 扩展函数简单示例
  • IV . 扩展函数调用选择方式 : 静态解析
  • V . 扩展函数 调用优先级
  • VI . 扩展函数 接收者 空值处理

I . 扩展函数 总结

扩展函数总结 :

① 扩展函数定义方式 : fun 接收者类型.函数名(参数列表){函数体}

② 扩展函数调用方式 : 父类和子类定义了相同函数签名的扩展函数 , 根据变量声明的类型调用对应的扩展函数 , 不根据变量的实际类型调用 ;

③ 扩展函数与成员优先级对比 : 成员函数优先级高于扩展函数 , 相同签名的两个函数 , 优先调用成员函数 ;

④ 可空接收者类型 : 可以为可空类型的接收者定义扩展函数 , 即声明扩展函数和调用扩展函数的类型后面都必须有 ? 修饰 ; ( 注意空值判定处理 )

II . 扩展函数概念简介

1 . 扩展函数声明格式 : 扩展函数在函数前多了接收者类型 , 函数体中可以使用 this 调用 接收者类型对象中的成员 ;

代码语言:javascript复制
fun 接收者类型.扩展函数名 ( 扩展函数参数列表 ) {
	//扩展函数函数体 , 可使用 this 关键字调用对象中的成员
}

2 . 接收者类型 : 声明扩展函数时 , 需要指定该函数是为哪个类型扩展的 , 被扩展的类型就是接收者类型 ;

3 . 调用接收者类型对象成员 : 在函数体中使用 this 关键字 , 可以调用接收者类型对象中的成员 , 如在下面的示例中 , 在类外部的扩展函数中 , 调用 Student 对象中的 name 成员 , 可以使用 this.name ;

4 . 扩展函数本质 : 为 接收者类型 定义扩展函数 , 并没有真正的再改类中插入新的成员函数 , 仅能通过 接收这类型对象变量.扩展函数() 的方式来调用这个函数 ;

III . 扩展函数简单示例

扩展函数代码示例 :

① 接收者类型扩展 : 为已经定义好的 Student 类 , 定义一个扩展函数 print ;

② 扩展函数参数 : 该扩展函数传入一个 num : Int 参数 ;

③ this 关键字访问接收者类型对象成员 : 在扩展函数中使用 this 关键字访问 Student 类成员 , this.name 访问其 name 属性 , this.age 访问其 age 属性 ;

代码语言:javascript复制
class Student {
    var name : String = "Tom"
    var age : Int = 18
}

/*
    为 Student 类定义扩展函数
        接收者类型 : Student
        类型名称 : print
        参数列表 : num : Int
    this 关键字 :
        使用 this 关键字可以在扩展函数中访问对象中的成员 ( 注意可见性 )
 */
fun Student.print(num : Int){
    println("打印学生信息 : 姓名 : ${this.name}, 年龄 : ${this.age}, 学号 : ${num}")
}

fun main() {
    //调用 Student 对象的扩展函数
    var student = Student();

    //打印学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 1
    student.print(1)
}

④ 执行结果 :

代码语言:javascript复制
打印学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 1
IV . 扩展函数调用选择方式 : 静态解析

1 . 扩展函数定义 : 为 基类 和 派生类 分别定义相同签名的扩展函数 , 可以精确控制调用 基类 或 派生类 的扩展函数 ;

2 . 调用方式 : 根据接收者类型确定调用哪个扩展函数 ;

① 接收者类型基类 : 如果 接收者类型 声明为基类 , 那么就会调用基类的扩展函数 ;

② 这里注意 : 不管其值被赋值成基类对象 , 还是赋值成派生类对象 , 接收者类型被声明成基类类型 , 调用的扩展函数就是基类的扩展函数 ;

③ 接收者类型派生类 : 如果 接收者类型 声明为派生类 , 那么就会调用派生类的扩展函数 ;

3 . 代码示例 :

① 定义父类与子类 : 定义父类 Student , 子类 MaleStudent ;

② 分别为父类 , 子类定义扩展函数 : 为 Student 和 MaleStudent 分别定义 print(num : Int){} 扩展函数 ;

③ 扩展函数调用 : 只要类型声明成 Student 类型 , 那么其调用的扩展函数就是 Student.print(num : Int){} 扩展函数 , 不管该类型的对象是父类还是子类对象 ; 只要类型声明成 MaleStudent 类型 , 那么其调用的扩展函数就是 MaleStudent.print(num : Int){} 扩展函数

代码语言:javascript复制
//定义父类
open class Student {
    var name : String = "Tom"
    var age : Int = 18
}

//定义子类
class MaleStudent : Student(){
}

//父类扩展函数
fun Student.print(num : Int){
    println("打印学生信息 : 姓名 : ${this.name}, 年龄 : ${this.age}, 学号 : ${num}")
}

//子类扩展函数
fun MaleStudent.print(num : Int){
    println("打印男学生信息 : 姓名 : ${this.name}, 年龄 : ${this.age}, 学号 : ${num}")
}

fun main() {
    // 1 . 变量声明为父类类型 , 赋值父类对象

    //接收者类型声明为 Student , 但实际对象是 Student 类型的
    //  此时扩展函数调用 Student 接收类型 的扩展数据
    var student : Student = Student();

    //打印学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 1
    student.print(1)

    // 2 . 变量声明为父类类型 , 赋值子类对象

    //接收者类型声明为 Student , 但实际对象是 MaleStudent 类型的
    //  此时扩展函数调用 Student 接收类型 的扩展函数
    var maleStudent : Student = MaleStudent()

    //打印学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 2
    maleStudent.print(2)


    // 3 . 变量声明为子类类型 , 赋值子类对象

    //接收者类型声明为 MaleStudent , 实际对象是 MaleStudent 类型的
    //  此时扩展函数调用 MaleStudent 接收类型 的扩展函数
    var maleStudent2 : MaleStudent = MaleStudent()

    //打印男学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 3
    maleStudent2.print(3)
}

执行结果 :

代码语言:javascript复制
打印学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 1
打印学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 2
打印男学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 3
V . 扩展函数 调用优先级

1 . 成员函数 优先级高于 扩展函数 : 如果 接收者类型 的扩展函数 与 成员函数有相同的函数签名 ( 即 函数名 , 参数列表个数 , 类型 , 顺序 , 完全相同 ) , 调用该签名的函数时 , 总是调用成员函数 , 扩展函数永远无法调用 ;

2 . 扩展函数 成员函数 优先级 代码示例 :

① 代码示例 : 接收类型 Student 扩展函数的函数签名与成员函数都是 print(num : Int) , 成员函数优先级高于扩展函数 , 因此调用该方法签名的方法时 , 总是调用成员函数 , 扩展函数无法被调用到 ;

代码语言:javascript复制
open class Student {
    var name : String = "Tom"
    var age : Int = 18

    fun print(num : Int){
        println("成员函数打印学生信息 : 姓名 : ${this.name}, 年龄 : ${this.age}, 学号 : ${num}")
    }
}

//该扩展函数与成员函数签名相同 , 永远不会被调用到
fun Student.print(num : Int){
    println("扩展函数打印学生信息 : 姓名 : ${this.name}, 年龄 : ${this.age}, 学号 : ${num}")
}

fun main() {
    var student : Student = Student();

    //成员函数打印学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 1
    student.print(1)
}

② 执行结果 :

代码语言:javascript复制
成员函数打印学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 1
VI . 扩展函数 接收者 空值处理

1 . 空值处理的两种类型 :

① 非空类型 : 这是 Kotlin 的默认类型 , 如 Student 类型是非空类型 , 不能被赋值为 null ;

② 可空类型 : 在类型名称后使用 ? 修饰 , 就是可空类型 , 如 Student? 类型是可空类型 , 该类型可以被赋值成 null ;

2 . 可空接收者类型的扩展函数 :

① 可空类型 : 一般情况下 , 扩展函数的接收者不能为空 , 也可以将接收者类型设置为可空类型 ;

② 扩展函数中判空 : 如果接收者类型可以为空 , 那么尽量在扩展函数中进行判空处理 , 防止出现空值异常 ;

3 . 代码示例 : 接收者类型是 Student? 类型 , 那么相应的变量类型也要是 Student? 类型 ;

代码语言:javascript复制
open class Student {
    var name : String = "Tom"
    var age : Int = 18
}

//可空接收者类型的扩展函数
fun Student?.print(num : Int){
    if(this == null){
        println("当前学生对象没有实例化")
    }else {
        println("扩展函数打印学生信息 : 姓名 : ${this.name}, 年龄 : ${this.age}, 学号 : ${num}")
    }
}

fun main() {
    /*
        默认情况下 变量是非空类型的变量 , 不能被赋值成 null
        如果变量类型后使用 ? 修饰 , 说明变量可以呗设置为空 , 可以呗赋值 null
     */
    var student : Student? = Student()

    //必须是 Student? 类型的变量才能被赋值成 null
    //  Student 类型的变量不能被赋值 null
    student = null

    //当前学生对象没有实例化
    student.print(1)
}

执行结果 :

代码语言:javascript复制
当前学生对象没有实例化

0 人点赞