Kotlin学习系列之:数据类(data class)和密封类(sealed class)

x33g5p2x  于2022-03-08 转载在 其他  
字(2.0k)|赞(0)|评价(0)|浏览(448)

一、数据类(data class)

  在介绍数据类之前,我们先来看看这样一段代码:

class Student(val name: String, var age: Int)

fun main(args: Array<String>) {

    val student = Student("David", 12)
    println(student)
}

此时会打印:
com.xlh.test.data.Student@49476842

大家都知道,这样直接打印student,会导致Student类的toString方法得到调用,最终打印到控制台的结果就是toString方法的返回结果。而Object类中的toString方法的实现(这里你可能产生疑问,Kotlin中的超级父类是Any类型,而不是Object类型,它俩的关系参看这篇博客):

所以就会出现前面的输出结果。

现在我们在类的声明前添加data关键字:

data class Student(val name: String, var age: Int)

其他不变,再看输出:

发生变化了,那就说明添加了data关键字后,Student类toString方法被重写了,并且形式为(属性名=属性值...),逗号分隔。实际上此时Student类就是一个数据类。

  1. 如何定义数据类

在类的声明前添加data关键字,即可将一个类定义成数据类。

  1. 数据类需要满足的要求
    a) 主构造器至少有一个参数
    b) 主构造器中的参数都需要使用var、val修饰

c) 数据类不能是abstract、open、sealed和inner的

  1. 反编译,查看类的结构

可以看到,一旦添加了data关键字,类的结构会发生变化,添加了很多方法,这就和扩展不一样了。我们来总结一下一共添加了哪些方法:

  • setter/getter
  • toString()
  • equals()
  • hashCode()
  • componentN()
  • copy()

前面三个方法都是kotlin.Any类中的定义的,在数据类中会被重写。接下来我们来介绍一下后面两个稍微陌生的方法:

  • 如何理解copy()方法:尽管主构造中声明的属性既可以被定义为var、val,但是最好的方式就是val,即为只读属性。声明为只读属性意味着一旦对象被创建后,它就会保持它的初始状态,特别是在多线程环境下不用担心它的值被其他线程而修改。假设把这种习惯或者说编码风格叫做一种约定,那么在这种背景下随之而来就会有一个问题:如果我们确实想修改只读属性的值,那么该怎么办?copy()方法就可以解决。
val copyStudent = student.copy(name = "xlh", age = 12)
  
println(copyStudent)

    打印:

这样就会创建一个新的对象并且修改了它的只读属性然后返回。注意:
    i. 原来的Student对象的属性值并没有改变

  ii. 参数的传递问题:可以不传,表示不修改属性值;如果传递的参数不是按照声明的顺序,那么必须使用有名参数的形式来调用,其他的情况下任意。 

总结copy方法的作用:就是创建一个当前对象的一份拷贝,并且在copy的过程中可以选择性改变拷贝对象的属性值,而原来对象的属性值不会改变。注意这里是浅拷贝。

  • 理解componentN方法:能够保证数据类可以使用解构声明(destructuring declarations)。
val (name, age) = student
println("$name, $age")

此时就可以输出:

  1. 关于data class最后再补充一点:

  在第3点中列出的方法里,只针对定义在主构造函数中的属性,如果你定义了一个属性:

data class Student(val name: String, var age: Int) {

    var address = "beijing"
}

这个address属性就不会被考虑。

二、密封类(sealed class)

  1. 语法含义:密封类的子类要该密封类放置在同一个文件中。
  2. 示例:
    SealedTest.kt
sealed class Animal

class Dog : Animal() {

    fun bark() {
        println("wangwangwang")
    }
}

class Cat : Animal() {

    fun miao() {
        println("miaomiaomiao")
    }
}

fun sayHello(animal: Animal) {

    when (animal) {
        is Dog -> animal.bark()	//Smart Cast
        is Cat -> animal.miao()
	//Don’t need else
    }
}

fun main(args: Array<String>){
    sayHello(Dog())
    sayHello(Cat())
}
  1. 密封类从某种程度上可以说子类的枚举化,从类的层次上进行了限制。

  2. 密封类子类的子类可以定义在任何地方,并不需要和密封类定义在同一个文件中。

相关文章