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

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

文章目录

I . 扩展函数 总结

II . 扩展函数概念简介

III . 扩展函数简单示例

IV . 扩展函数调用选择方式 : 静态解析

V . 扩展函数 调用优先级

VI . 扩展函数 接收者 空值处理



I . 扩展函数 总结


扩展函数总结 :



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


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


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


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




II . 扩展函数概念简介


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


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


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



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



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




III . 扩展函数简单示例


扩展函数代码示例 :



① 接收者类型扩展 : 为已经定义好的 Student 类 , 定义一个扩展函数 print ;
② 扩展函数参数 : 该扩展函数传入一个 num : Int 参数 ;
③ this 关键字访问接收者类型对象成员 : 在扩展函数中使用 this 关键字访问 Student 类成员 , this.name 访问其 name 属性 , this.age 访问其 age 属性 ;
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)
}



④ 执行结果 :


打印学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 1



IV . 扩展函数调用选择方式 : 静态解析


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



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



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


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


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



3 . 代码示例 :



① 定义父类与子类 : 定义父类 Student , 子类 MaleStudent ;
② 分别为父类 , 子类定义扩展函数 : 为 Student 和 MaleStudent 分别定义 print(num : Int){} 扩展函数 ;
③ 扩展函数调用 : 只要类型声明成 Student 类型 , 那么其调用的扩展函数就是 Student.print(num : Int){} 扩展函数 , 不管该类型的对象是父类还是子类对象 ; 只要类型声明成 MaleStudent 类型 , 那么其调用的扩展函数就是 MaleStudent.print(num : Int){} 扩展函数
//定义父类
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)
}


执行结果 :


打印学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 1

打印学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 2

打印男学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 3

1

2

3



V . 扩展函数 调用优先级


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



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



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


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)
}

② 执行结果 :


成员函数打印学生信息 : 姓名 : Tom, 年龄 : 18, 学号 : 1

1



VI . 扩展函数 接收者 空值处理


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



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


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



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



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


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



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


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)
}


执行结果 :


当前学生对象没有实例化


目录
相关文章
|
6天前
|
安全 Kotlin
Kotlin - 作用域函数
Kotlin - 作用域函数
|
3天前
|
Java Kotlin Python
Kotlin - 扩展成员
Kotlin - 扩展成员
9 2
Kotlin - 扩展成员
|
6天前
|
Java Kotlin Python
Kotlin - 扩展成员
Kotlin - 扩展成员
19 2
Kotlin - 扩展成员
|
10天前
|
Kotlin
Kotlin - 高阶函数与函数引用
Kotlin - 高阶函数与函数引用
23 3
Kotlin - 高阶函数与函数引用
|
5天前
|
IDE 开发工具 Kotlin
Kotlin - 函数与Lambda表达式
Kotlin - 函数与Lambda表达式
|
8天前
|
Kotlin
Kotlin教程笔记(21) -高阶函数与函数引用
Kotlin教程笔记(21) -高阶函数与函数引用
|
8天前
|
安全 Kotlin
Kotlin教程笔记(23) -作用域函数
Kotlin教程笔记(23) -作用域函数
|
10天前
|
安全 Kotlin
Kotlin - 作用域函数
Kotlin - 作用域函数
23 3
|
12天前
|
IDE 开发工具 Kotlin
Kotlin - 函数与Lambda表达式
Kotlin - 函数与Lambda表达式
|
3天前
|
安全 Kotlin
Kotlin - 作用域函数
Kotlin - 作用域函数
12 0