Kotlin中扩展函数、infix关键字、apply函数和DSL的详解

简介: Kotlin中扩展函数、infix关键字、apply函数和DSL的详解

一、扩展函数

定义扩展函数

扩展可以在不直接修改类定义的情况下增加类功能,扩展可以用于自定义类,也可以用于比如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
    nullable

输出结果如下

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表达式来读取和配置它们。


目录
相关文章
|
6天前
|
XML 编译器 Android开发
Kotlin DSL 实战:像 Compose 一样写代码
Kotlin DSL 实战:像 Compose 一样写代码
71 0
|
8月前
|
Java API 调度
Kotlin 中的suspend 关键字
Kotlin 中的suspend 关键字
80 0
|
6天前
|
Kotlin
Kotlin - 标准函数(with、run和apply)
Kotlin - 标准函数(with、run和apply)
6 1
|
7月前
|
存储 Java 编译器
Kotlin 学习笔记(四)—— 作用域函数、inline 关键字、反引号等 Kotlin 基本用法(下)
Kotlin 学习笔记(四)—— 作用域函数、inline 关键字、反引号等 Kotlin 基本用法(下)
35 0
|
7月前
|
Java Android开发 开发者
Kotlin 学习笔记(四)—— 作用域函数、inline 关键字、反引号等 Kotlin 基本用法(上)
Kotlin 学习笔记(四)—— 作用域函数、inline 关键字、反引号等 Kotlin 基本用法(上)
53 0
|
9月前
|
安全 Java 编译器
Kotlin 学习笔记(一)—— 基本类型、函数、lambda、类与对象的写法
Kotlin 作为 Android 开发的首选语言,其基础语法和特性需要重点学习。本文概括总结了 Kotlin 的基本类型、关键字、函数、闭包、高阶函数、类与对象等核心知识点,并给出示例代码进行讲解。
154 0
Kotlin 学习笔记(一)—— 基本类型、函数、lambda、类与对象的写法
|
9月前
|
Java Kotlin
Kotlin中与Java互操作与可空性、类型映射、属性访问、@JvmOverloads、@JvmField、@JvmStatic、@Throws和函数类型操作详解
Kotlin中与Java互操作与可空性、类型映射、属性访问、@JvmOverloads、@JvmField、@JvmStatic、@Throws和函数类型操作详解
71 0
|
Kotlin
【Kotlin】Kotlin 构造函数 ( 主构造函数 | 主构造函数声明属性 | init 初始化代码块 | 次构造函数 | 构造函数委托 | 调用构造函数创建实例对象 )(二)
【Kotlin】Kotlin 构造函数 ( 主构造函数 | 主构造函数声明属性 | init 初始化代码块 | 次构造函数 | 构造函数委托 | 调用构造函数创建实例对象 )(二)
103 0
|
Kotlin
【Kotlin】Kotlin 构造函数 ( 主构造函数 | 主构造函数声明属性 | init 初始化代码块 | 次构造函数 | 构造函数委托 | 调用构造函数创建实例对象 )(一)
【Kotlin】Kotlin 构造函数 ( 主构造函数 | 主构造函数声明属性 | init 初始化代码块 | 次构造函数 | 构造函数委托 | 调用构造函数创建实例对象 )(一)
254 0
|
4天前
|
移动开发 API Android开发
构建高效Android应用:探究Kotlin协程的优势与实践
【5月更文挑战第17天】在移动开发领域,性能优化和流畅的用户体验一直是开发者追求的目标。针对Android平台,Kotlin语言凭借其简洁性和功能丰富性成为了许多开发者的首选。其中,Kotlin协程作为异步编程的强大工具,为处理并发任务提供了轻量级的解决方案。本文深入探讨了Kotlin协程的核心优势,并通过实例分析其在Android开发中的应用,旨在帮助开发者提升应用的性能和响应能力。