kotin学习(十)——数据类和密封类(详细)

我们经常创建⼀些只保存数据的类。在这些类中,⼀些标准函数往往是从数据机械推导⽽来的。在 Kotlin中,这叫做 数据类并标记为 data

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

上面简单的一行代码的功能是很强大的。就一行代码会有下面的成员导出:
1. equals() / hashCode() 对。
2. toString() 格式是 “User(name=John, age=42)” 。
3. componentN() 函数 按声明顺序对应于所有属性,这样的方法用于数据解构。
4. copy() 函数(⻅下⽂)会自动实现深克隆。

但是如果这些函数中的任何⼀个在类体中显式定义(自己写)或继承⾃其基类型(继承某一个类,那个被继承的类中含有以上函数中的某几个),则不会⽣成该函数

为了正常生成以上的函数,数据类要满足以下的条件:(保证生成代码的一致性以及数据类为了要有意义)
1. 主构造函数需要⾄少有⼀个参数;(因为没有属性的数据类没有意义)
2. 主构造函数的所有参数需要标记为 val 或 var ;(因为没有属性的数据类没有意义)
3. 数据类不能是抽象、开放、密封或者内部的;(抽象数据类没有意义;不开放是因为不需要被继承;比密封是为了实例化对象;不能是内部的是因为需要方便创建对象,不需要先创建外部类在创建内部类对象,数据类要保证立即初始化。)
4. (在1.1之前)数据类只能实现接⼝。(但是1.1之后能扩展其他的类,数据类可以扩展包括密封类在内的其他类)
5. 在 JVM 中,如果⽣成的类需要含有⼀个⽆参的构造函数,则所有的属性必须指定默认值。(之前类与对象中说到过)

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

data class Teacher(var name: String,var user: User)

fun main(args: Array<String>) {
    var user1 = User("huahua",11)
    var user2 = User("wawa",15)
    var user3 = user1.copy(age=22) //数据类会生成copy()函数
    println(user1.equals(user2)) //数据类会有equals()方法
    println(user1.toString()) //数据类会有toString()方法
    println("${user1.hashCode()} and ${user2.hashCode()} ") //数据类也会有hashCode()方法
    println("${user3}") //复制出的对象和被复制的属性值一样,但是内存会重新开辟

    var t1 = Teacher("MrLi",user1)

    println(t1)
    var t2 = t1.copy()
    println(t2)
}

复制

在很多情况下,我们需要复制⼀个对象改变它的⼀些属性,但其余部分保持不变。copy() 函数就是为此⽽⽣成。对于上⽂的 User 类,其实现会类似下⾯这样:

fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
data class User(var name:String,var age:Int)

fun main(args: Array<String>) {
    var u1 = User("MrWang",14)
    var u2 = u1.copy(age = 16) //如果在复制时要改变一些属性的值,可以这样调用
    println(u1) //输出 User(name=MrWang, age=14)
    println(u2) //输出 User(name=MrWang, age=16)
}

数据类和解构声明

上面提到 componentN() 函数 按声明顺序对应于所有属性,这样的方法用于数据解构。

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

fun main(args: Array<String>) {
    var u1 = User("MrWang",14)
    var (name,age) = u1  //这里会调用生成的componentN()函数
    println("$name $age")
}

标准数据类

标准库提供了 Pair 和 Triple 。尽管在很多情况下命名数据类是更好的设计选择,因为它们通过为属性提供有意义的名称使代码更具可读性。
官方文档没有说过Pair和Triple是什么,小编看了源码,粗略了了解了一下。
Pair是一个标准的二元数据类,Triple是一个三元数据类方法都差不多

fun main(args: Array<String>) {
    var p = Pair("huahua",11)
    var p1 = "wawa".to(11) //Pair的to方法
    println(p.toString())
    var list = p.toList()  //Pair有toList方法
    println(list)
}

密封类

密封类用来表示受限的类继承结构:当⼀个值为有限集中的类型、而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在⼀个实例,⽽密封类的⼀个⼦类可以有可包含状态的多个实例

get上面的信息:
1. 密封类有点像枚举类,和枚举类的有区别
2. 枚举类是一个类的多个实例,比如Season枚举类只有“春”,“夏”,“秋”,“冬”这四个实例
3. 密封类是为了集中类型的,密封类是要被继承的,子类的类型就是密封类的类型(像“人种”可以定义为密封类,“黑人”,”黄种人”等可以继承自人种的密封类,而且“黑人”能有多个实例)

sealed class Expr
data class Const(val number: Double) : Expr() //ConstExpr类型的,而且可以创建对个实例
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

上面也再次说明kotlin1.1之后数据类能继承密封类在内的其他类(1.1之前只能实现接口)
⼀个密封类是⾃⾝抽象的,它不能直接实例化并可以有抽象(abstract)成员
密封类不允许有⾮-private 构造函数(其构造函数默认为 private)(为了不让外部调用构造器)

使用密封类的关键好处在于使⽤ when 表达式 的时候,如果能够验证语句覆盖了所有情况,就不需要为该语句再添加⼀个 else ⼦句了。当然,这只有当你⽤ when 作为表达式(使⽤结果)而不是作为语句时才有用

fun eval(expr: Expr): Double = when(expr) {
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN
    // 不再需要 `else` ⼦句,因为我们已经覆盖了所有的情况
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值