【Kotlin 集合概述】可变参数vararg、中缀函数infix以及解构声明(二十)
导读大纲
- 1.1 使用集合: vararg、infix 调用和解构声明
- 1.1.1 扩展 Java 集合 API
- 1.1.2 vararg: 接受任意数量参数的函数
- 1.1.3 处理pairs: Infix 调用和解构声明
1.1 使用集合: vararg、infix 调用和解构声明
-
本节将介绍 Kotlin 标准库中用于处理集合的一些函数
-
同时,还介绍一些相关的语言特性(主要涉及以下三大特性)
- vararg 关键字允许你声明一个包含任意数量参数的函数
- infix 中缀函数, 可让您在调用某些单参数函数时更加简单
- 解构声明(Destructuring declarations)可将单个复合值解包为多个变量
1.1.1 扩展 Java 集合 API
- 我们认为 Kotlin 中的集合是与 Java 中相同的类,但具有扩展的 API
- 比如,这里的获取列表中最后一个元素和计算数字集合之和
fun main() {
val strings: List<String> = listOf("first", "second", "fourteenth")
strings.last()
// fourteenth
val numbers: Collection<Int> = setOf(1, 14, 2)
numbers.sum()
// 17
}
- 为什么在 Kotlin 中,尽管集合是 Java 库类的实例,却可以用集合做很多事情
- 现在,答案应该很清楚: last 和 sum 函数被声明为扩展函数
- 关于扩展函数和扩展属性以及顶级函数与顶级属性前面已经讲过,这里不再赘述
- 顶级函数和顶级属性
- 扩展函数系列
- 学习扩展函数时,我曾说过,定义的扩展函数必须导入到当前文件才会生效
- 这里无需导入是因为集合相关的扩展函数
总是默认导入到你的 Kotlin 文件
中
- 这里无需导入是因为集合相关的扩展函数
- 关于扩展函数和扩展属性以及顶级函数与顶级属性前面已经讲过,这里不再赘述
- last函数是 List 类的扩展,这里的T是泛型的类型参数,可以先不用理会
- 这个扩展的实现很简单,应该都能看懂
- 比如在扩展函数中调用this.isEmpty(),this指代的是List的实例对象,可以省略
- sum函数只针对Iterable类型,简单说就是只有元素是Int类型的迭代对象可以调用该方法
- 现在,答案应该很清楚: last 和 sum 函数被声明为扩展函数
public fun <T> List<T>.last(): T {
if (isEmpty())
throw NoSuchElementException("List is empty.")
return this[lastIndex]
}
@kotlin.jvm.JvmName("sumOfInt")
public fun Iterable<Int>.sum(): Int {
var sum: Int = 0
for (element in this) {
sum += element
}
return sum
}
1.1.2 vararg: 接受任意数量参数的函数
- 调用函数创建列表时,可以
传递任意数量的参数
- 如果在标准库中查找该函数的声明方式,会发现其签名如下
- fun listOf(vararg values: T): List { /* implementation */ }
- fun listOf(vararg values: T): List { /* implementation */ }
- 如果在标准库中查找该函数的声明方式,会发现其签名如下
val list = listOf(2、3、5、7、11)
- 这种方法利用一种语言特性: vararg
- 即将任意数量的值打包到数组中然后传递给方法
- <1> Kotlin 的 vararg 与 Java 中的可变参数类似,但语法略有不同
- Kotlin 在参数上使用 vararg 修饰符
- 而 Java 是在类型后面加上三个点
// 在Kotlin中
fun main() {
val strings: List<String> = listOf("first", "second", "fourteenth") // <1>
strings.last()
}
// 在Java中
public class Example {
public static void Test (String ...args) { // <1>
System.out.println(Arrays.toString(args));
}
public static void main(String[] args){
Test("1", "2", "3");
}
}
- Kotlin 和 Java 的另一个不同之处在于
- 当需要传递的参数已经打包在数组中时,调用函数的语法
- 在Java中,可以原封不动地传递数组, 而Kotlin则要求显式地解包数组
- 以便每个数组元素都成为被调用函数的单独参数
- wtf,这不就是Python的*args,对, 没错
- <1> 这一功能被称为展开运算符,使用它直接在相应参数前加上"*"字符
- 这里"展开"args数组(其中包含传递给main函数的命令行参数)
- 将其用作 listOf 函数的可变参数
- 这里"展开"args数组(其中包含传递给main函数的命令行参数)
- <2> 展开运算符可将数组中的值与一些其他值组合起来
- Java 中不支持这种操作,这就是最大的不同点
fun main(args: Array<String>) {
val list = listOf("args: ", *args) // <1>
println(list)
val extraArgs = listOf("hello", *args) // <2>
println(extraArgs)
}
1.1.3 处理pairs: Infix 调用和解构声明
- 之前学习集合的简单创建时, 讲过可以使用 mapOf 函数
- <1> to 并不是内置结构,而是一种特殊的方法调用,即 infix 调用
- 如果小伙伴记性不错,应该记得之前for循环迭代整数时使用过"100 downTo 1"语法
- 没戳,infix fun Int.downTo(to: Int) 也是一个中缀函数,即使用infix修饰符的函数
- <1> to 并不是内置结构,而是一种特殊的方法调用,即 infix 调用
val map = mapOf(1 to "one", 7 to "seven", 53 to "fifty-three") // <1>
- 在 infix 调用中
- 方法名紧接在目标对象名和参数之间, 没有额外的分隔符
- <1> 以下两个调用是等价的
- 第一个是按常规方式调用函数
- 第二个使用infix简洁语法来调用函数
fun main(args: Array<String>) {
println(1.to("one")) // <1>
println(1 to "one") // <1>
}
=============================
(1, one)
(1, one)
- 对于只有一个所需参数的普通方法和扩展函数,可以使用 infix 调用
- 要使用中缀简洁语法调用一个函数,需要用 infix 修饰符对其进行标记
- <1> to 函数返回 Pair 的实例,Pair 是 Kotlin 标准库中的一个类
- Pair表示一对元素
- Pair表示一对元素
infix fun Any.to(other: Any) = Pair(this, other) // <1>
- 请注意,您可以直接用一个 Pair 对象来初始化两个变量
- <1> 这一功能称为解构
- <1> 这一功能称为解构
fun main(args: Array<String>) {
val (number, name) = 1 to "one" // <1>
println("$number to $name")
}
- 解构功能并不局限于Pair对象
- 例如, 也可以用 map entry来初始化 key 和 value 这两个变量
- <1> 这也适用于循环,正如在实现joinToString时使用的 withIndex 函数
- 这里解构声明的作用就在于不用额外定义一个 index 变量
- 通过迭代时自增来跟踪当前元素的索引值
- 解构出来的index本身就代表当前元素的索引
- 这里解构声明的作用就在于不用额外定义一个 index 变量
for ((index, element) in collection.withIndex()) { // <1>
println("$index: $element")
}
- to 函数是一个扩展函数, 可以创建一对任意元素
- 这意味着它是通用接收器的扩展:
- 可以编写 1 to “one”, “one” to 1, list to list.size(),以此类推
- <1> 看看 mapOf 函数的实际签名, 与 listOf 类似
- mapOf也接受可变参数,但这次参数是键值对(Pair<K, V>)
- 尽管在 Kotlin 中创建一个新的 map 看起来像是一个特殊的结构
- 但它却是一个infix语法的常规函数,Kotlin的简洁性也算是初露锋芒
- 这意味着它是通用接收器的扩展:
// <1>
public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V> =
if (pairs.size > 0) pairs.toMap(LinkedHashMap(mapCapacity(pairs.size))) else emptyMap()