欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Kotlin学习(二十)——解构声明和集合

程序员文章站 2024-03-20 22:19:46
...

以前在面向对象的那篇文章中,我们说到了数据类,也提到了数据类的j解构
现在我们详细的说一下解构
如果我们定义了data class ,编译器会自动添加operator fun component()N函数,来帮助对象解构:

data class Person(var name:String,var age :Int)

fun main(args: Array<String>) {
    var (name,age) =Person("huahua",11) //这能调用解构函数
    println("$name  $age")
}

⼀个解构声明会被编译成以下代码:

val name = person.component1()
val age = person.component2()

如果定义的不是数据类(一般情况下不要不使用数据类,因为自己定义数据类很烦琐):

//如果我们不是定义数据类,我们就要自己提供componentN()函数
class User(var name:String,var age: Int){
    operator fun component1():String{
        return this.name
    }
    operator fun component2():Int{
        return this.age
    }
}
fun main(args: Array<String>) {
    //有时把⼀个对象 解构 成很多变量会很⽅便,例如:
    var person = Person("Huahua",11)
    var (i,j) = person //如果解构时接受签名的名称和属性一至或者相近,会按属性走,不一样就会按顺序走
    println(i)
    println(j)
    var user = User("jj",15)
    var (ii,jj) = user
    println("$ii $jj")
}

解构声明也可以使用在for循环中:

fun main(args: Array<String>) {
    var list = ArrayList<Person>()
    for(i in 0 until 6){
        list.add(Person("huahua$i",11+i))
    }

    for ((a,b) in list){
        println("$a  $b")
    }
    //变量 a 和 b 的值取⾃对集合中的元素上调⽤ component1() 和 component2() 的返回值
}

从函数中返回两个变量

让我们假设我们需要从⼀个函数返回两个东西。例如,⼀个结果对象和⼀个某种状态。在 Kotlin 中⼀个简洁的实现⽅式是声明⼀个数据类并返回其实例:

data class Result(var sum:Int, var div:Int)

fun getResult(a : Int,b : Int):Result{
    return Result(a+b,a-b)
}
fun main(args: Array<String>) {
    var (sum,div) = getResult(3,2)
    println("$sum $div")
}

因为数据类⾃动声明 componentN() 函数,所以这⾥可以⽤解构声明。
注意:我们也可以使⽤标准类 Pair 并且让 function() 返回 Pair

解构声明和映射

可能遍历⼀个映射(map)最好的⽅式就是这样:

fun main(args: Array<String>) {
    var map = mapOf(1 to "one", 2 to "two", 3 to "three")
    for ((key,value) in map){
        println(" $key $value")
    }
    var map2 = mutableMapOf()

    var map1 = map.mapValues {a -> "${a.key} ${a.value}" }
    println(map1)
}

为使其(map)能⽤,我们应该
1. 通过提供⼀个 iterator() 函数将映射表⽰为⼀个值的序列,
2. 通过提供函数 component1() 和 component2() 来将每个元素呈现为⼀对。
当然事实上,标准库提供了这样的扩展:( 不要我们提供了,kt标准库中已经有了)

operator fun <K, V> Map<K, V>.iterator(): Iterator<Map.Entry<K, V>> = entrySet().iterator()
operator fun <K, V> Map.Entry<K, V>.component1() = getKey()
operator fun <K, V> Map.Entry<K, V>.component2() = getValue()

因此你可以在 for-循环中对映射(以及数据类实例的集合等)⾃由使⽤解构声明。

下划线用于未使用的变量(自 1.1 起)

比如上面getResult()函数:

data class Result(var sum:Int, var div:Int)

fun getResult(a : Int,b : Int):Result{
    return Result(a+b,a-b)
}
fun main(args: Array<String>) {
    var (_,div) = getResult(3,2)//下划线表明跳过第一个解构函数,直接调用第二个解构
    println("$div")
}

在 lambda 表达式中解构(自 1.1 起)

你可以对 lambda 表达式参数使用解构声明语法。如果 lambda 表达式具有 Pair 类型(或者 Map.Entry 或任何其他具有相应 componentN 函数的类型)的参数,那么可以通过将它们放在括号中来引⼊多个新参数来取代单个新参数:

fun main(args: Array<String>) {
    var map = mapOf(1 to "one", 2 to "two", 3 to "three")

    var map1 = map.mapValues {a -> "${a.key} ${a.value}" }
    println(map1)
    var map2 = map.mapValues { (key,value) -> "${value}" }//解构entry (key,value)是一个解构对
    println(map2)

}

注意声明两个参数和声明⼀个解构对来取代单个参数之间的区别:(lambda表达式中很重要)

{ a //-> …… } // ⼀个参数
{ a, b //-> …… } // 两个参数
{ (a, b) //-> …… } // ⼀个解构对
{ (a, b), c //-> …… } // ⼀个解构对以及其他参数

如果解构的参数中的⼀个组件未使⽤,那么可以将其替换为下划线,以避免编造其名称:

fun main(args: Array<String>) {
    var map = mapOf(1 to "one", 2 to "two", 3 to "three")
    var map3 = map.mapValues { (_,value) ->"$value"}
    println(map3)
}

你可以指定整个解构的参数的类型或者分别指定特定组件的类型:

fun main(args: Array<String>) {
    var map = mapOf(1 to "one", 2 to "two", 3 to "three")
    var map4 = map.mapValues { (_,value):Map.Entry<Int,String>->"$value" }
    var map5 = map.mapValues{(_,value:String)->"$value"}
}

集合:List、Set、Map

与大多数语言不同,Kotlin 区分可变集合和不可变集合(lists、sets、maps 等)。精确控制什么时候集合可编辑有助于消除 bug 和设计良好的 API。

预先了解⼀个可变集合的只读视图⼀个真正的不可变集合之间的区别是很重要的。它们都容易创建,但类型系统不能表达它们的差别,所以由你来跟踪(是否相关)。

Kotlin 的 List<out T> 类型是⼀个提供只读操作如 size 、get 等的接口。和 Java 类似,它继承自 Collection<T> 进而继承自Iterable<T> 。改变 list 的方法是由 MutableList<T> 加⼊的。这⼀模式同样适用于 Set<out T>/MutableSet<T>Map<K, out V>/MutableMap<K, V> 。
我们可以看下 list 及 set 类型的基本⽤法:

fun main(args: Array<String>) {
    val numbers: MutableList<Int> = mutableListOf(1, 2, 3) //创建一个可变集合
    val readOnlyView: List<Int> = numbers //可变集合能赋值给一个不可变集合
    println(numbers) // 输出 "[1, 2, 3]"
    numbers.add(4)  //可变集合能调用add,remove,等接口
    println(readOnlyView) // 输出 "[1, 2, 3, 4]" 因为readOnlyView指向的是numbers的地址
    readOnlyView.clear() // -> 不能编译 因为集合不可变
    val strings = hashSetOf("a", "b", "c", "c")  
    assert(strings.size == 3)
}

Kotlin 没有专门的语法结构创建 list 或 set。要用标准库的方法,如 listOf() 、mutableListOf() 、setOf() 、mutableSetOf() 。在非性能关键代码中创建 map 可以用⼀个简单的惯⽤法来完成:mapOf(a to b, c to d)

注意上面的 readOnlyView 变量(译者注:与对应可变集合变量 numbers )指向相同的底层 list 并会随之改变。如果⼀个 list 只存在只读引用,我们可以考虑该集合完全不可变。创建⼀个这样的集合的⼀个简单方式如下:

fun main(args: Array<String>) {
    var list = listOf<Int>(1,2,4,5,6)
    list.add()//编译错误,只有制度集合产生,实际上底层返回的是out T类型的,生产者:只读
}

目前 listOf 方法是使用 array list 实现的,但是未来可以利⽤它们知道自己不能变的事实,返回更节约内存的完全不可变的集合类型。

注意这些类型是协变的。这意味着,你可以把⼀个 List 赋值给 List 假定 Rectangle 继承自Shape。对于可变集合类型这是不允许的,因为这将导致运行时故障。

有时你想给调⽤者返回⼀个集合在某个特定时间的⼀个快照, ⼀个保证不会变的:

class Controller {
    private val _items = mutableListOf<String>()
    val items: List<String> get() = _items.toList()

    fun  add(item :String){
        _items.add(item)
    }
    fun removeAt(index:Int){
        _items.removeAt(1)
    }

    override fun toString(): String {
        return _items.toString()
    }
}

fun main(args: Array<String>) {
    var c  = Controller()
    c.add("hello")
    c.add("world")
    println(c)
    var cItems = c.items
    println(c)
    c.add("ni,hai")
    var cItems1 = c.items
    println(cItems==cItems1) //输出false,因为只是一个快照而已,不是同一个对象
}

这个 toList 扩展⽅法只是复制列表项,因此返回的 list 保证永远不会改变。
List 和 set 有很多有⽤的扩展⽅法值得熟悉,如下:

val items = listOf(1, 2, 3, 4)
items.first() == 1
items.last() == 4
items.filter { it % 2 == 0 } // 返回 [2, 4]
val rwList = mutableListOf(1, 2, 3)
rwList.requireNoNulls() // 返回 [1, 2, 3]
if (rwList.none { it > 6 }) println("No items above 6") // 输出“No items above 6”
val item = rwList.firstOrNull()

…… 以及所有你所期望的实用工具,例如 sort、zip、fold、reduce 等等。
Map 遵循同样模式。它们可以容易地实例化和访问,像这样:

val readWriteMap = hashMapOf("foo" to 1, "bar" to 2)
println(readWriteMap["foo"]) // 输出“1”
val snapshot: Map<String, Int> = HashMap(readWriteMap)