文章目录- 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){}
扩展函数
//定义父类
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复制当前学生对象没有实例化