博主前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住也分享一下给大家
点击跳转到教程
一、扩展函数
定义扩展函数
扩展可以在不直接修改类定义的情况下增加类功能,扩展可以用于自定义类,也可以用于比如List,String,以及Kotlin标准库里的其他类。和继承相似,扩展也能共享类行为,在你无法接触某个类定义,或者某个类没有使用open修饰符,导致你无法继承它时,扩展就是增加类功能的最好选择。
1、定义扩展函数和超类上定义扩展函数
/**
* 定义扩展函数
* 扩展可以在不直接修改类定义的情况下增加类功能,扩展可以用于自定义类,也可以用于
* 比如List,String,以及Kotlin标准库里的其他类。和继承相似,扩展也能共享类行为,
* 在你无法接触某个类定义,或者某个类没有使用open修饰符,导致你无法继承它时,扩展
* 就是增加类功能的最好选择。
*
*
* */
/**
* 给字符串追加若干个感叹号
*/
fun String.addExt(amount: Int = 1) = this + "!".repeat(amount)
/**
* 定义扩展函数和定义一般函数差不多,但有一点不一样,除了函数定义,你还需要指定接收功能扩展
* 的接收者类型
*/
fun Any.easyPrint() = println(this)
fun main() {
println("abc".addExt(2))
"abc".easyPrint()
15.easyPrint()
}
输出结果如下
abc!!
abc
15
2、标准函数与泛型扩展函数
/**
* 泛型扩展函数
* 如果想调用addExt扩展函数之前和之后分别打印字符串怎么办?
* 15.easyPrint().addExt(2).easyPrint() 其实就是怎么进行链式调用?
*
* 泛型扩展函数含义:
* 新的泛型扩展函数不仅可以支持任何类型的接收者,还保留了接收者的类型信息,
* 使用了泛型类型后,扩展函数能够支持更多类型的接收者,适用范围更广了。
*
*
*/
fun String.addExt(amount: Int = 1) = this + "!".repeat(amount)
fun <T> T.easyPrint(): T {
println(this)
return this
}
fun main() {
"15".easyPrint().addExt(2).easyPrint()
/**
*
* 泛型扩展函数在Kotlin标准库里随处可见,例如let函数,let函数被定义成了泛型扩展函数
* 所以能支持任何类型,它接收一个lambda表达式,这个lambda表达式接收者T作为值参,
* 返回的R为lambda表达式返回的任何新类型
*
* let函数的源码
* public inline fun <T, R> T.let(block: (T) -> R): R
* 泛型T这里是String类型
* R就是lambda表达式返回的结果类型 这里的R类型为Int类型
*/
val i = "abc".let {
50
}
println(i)
}
输出结果如下
15
15!!
50
3、扩展属性
/**
* 扩展属性
* 除了给类添加功能扩展函数外,你还可以给类定义扩展属性,给String类添加一个扩展属性
* 这个扩展属性可以统计字符串里有多少个元音字母
*/
val String.numVowels
get() = count {
"aeiou".contains(it)
}
fun <T> T.easyPrint(): T {
println(this)
return this
}
fun main() {
"The people's Republic of China".numVowels.easyPrint()
}
输出结果如下
10
4、可空类型扩展函数
/**
* 可空类扩展
* 你也可以定义扩展函数用于可空类型,在可空类型上定义扩展函数,你就可以直接
* 在扩展函数体内解决可能出现的空值问题。
*/
fun String?.printWithDefault(default: String) = println(this ?: default)
fun main() {
val nullableString: String? = null
nullableString.printWithDefault("abc")
val str = "jack"
str.printWithDefault("abc")
}
输出结果如下
abc
jack
5、infix关键字详解
/**
* infix关键字适用于有单个参数的扩展和类函数,可以让你以更简洁的语法调用函数
* 如果一个函数定义使用了infix关键字,那么调用它时,接收者和函数之间的点操作以及
* 参数的一对括号都可以不要
*/
infix fun String?.printWithDefault(default: String) = println(this ?: default)
fun main() {
val nullableString: String? = null
nullableString printWithDefault "abc"
// mapOf("jack" to 19)
}
输出结果如下
abc
6、定义扩展文件和重命名扩展
1、创建kotlin文件名字为IterableExt
/**
* 定义扩展文件
* 扩展函数需要在多个文件里面使用,可以将它定义在单独的文件,然后import
*/
fun <T> Iterable<T>.randomTake(): T = this.shuffled().first()
2、使用
/**
* 重命名扩展
* 有时候,你想使用一个扩展或一个类,但它的名字不和你的意愿可以用as 取别名
*/
import com.example.kotlinlearn.extension.randomTake as randomizer
/**
* @Author: ly
* @Date: 2023/2/2
* @Description:
*/
fun main() {
val list = listOf("Jason", "Jack", "Tom")
val set = setOf("Jason", "Jack", "Tom")
println(list.randomizer())
println(set.randomizer())
}
输出结果如下
Jack
Jason
二、带接收者的函数字面量
apply函数是如何做到,支持接收者对象的隐式调用的
具体详解在代码中,注释已经给出
/**
* 扩展函数
*/
fun String.addEdit(amount: Int = 1) = "!".repeat(this.count())
/**
* 泛型扩展函数 返回Unit类型 对应java中void
*/
fun <T> T.easyPrints(): Unit = println(this)
/**
* 为什么要传入扩展函数(泛型),而不是一个普通的扩展函数
* T.() -> Unit
* 扩展函数里自带了接收者对象的this隐式调用
* 为什么是泛型的扩展函数?
*
* 匿名函数也可以是扩展函数
* () -> Unit 普通的匿名函数
* File.()->Unit 匿名函数 在File上面使用的匿名函数 也是扩展函数 匿名函数内部this指向一个File对象,隐式调用
*/
public inline fun <T> T.apply(block: T.() -> Unit): T {
return this
}
//public inline fun File.apply(block: File.() -> Unit): File {
// return this
//}
fun main() {
val file = File("xx").apply {
setReadable(true)
}
/**
* 这里分解一下
* 1.定义扩展函数
*/
fun File.ext(): Unit {
setReadable(true)
}
/**
* 2.给block变量赋值
*/
val block = File::ext
/**
* 3.传入apply函数
*/
File("xx").apply {
block
}
}
DSL 使用这样的编程范式,就可以写出业界知名的“领域特定语言”(DSL),一种API编程范式,暴露接收者的函数和特性,以便于使用你定义的lambda表达式来读取和配置它们。