Kotlin学习之 11数据类与密封类

104 篇文章 14 订阅
15 篇文章 0 订阅

Kotlin 数据类与密封类

一.数据类

1.Kotlin 可以创建一个只包含数据的类,关键字为 data:

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

2.编译器会自动的从主构造方法中根据所有声明的属性提取以下方法:

equals() / hashCode()
toString() 格式如 "User(name=John, age=42)"
componentN() functions 对应于属性,按声明顺序排列
copy() 方法

3.如果这些方法在类中已经被明确定义了,或者从超类中继承而来,就不再会生成。

为了保证生成代码的一致性以及有意义,数据类需要满足以下条件:

主构造方法至少包含一个参数。

所有的主构造方法的参数必须标识为val 或者 var ;

数据类不可以声明为 abstract, open, sealed 或者 inner;

数据类不能继承其他类 (但是可以实现接口)。

二.复制

1.复制使用 copy() 方法,我们可以使用该方法复制对象并修改部分属性,

对于上文的 User 类,其实现会类似下面这样:

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

2.实例

使用 copy 类复制 User 数据类,并修改 age 属性:

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


fun main(args: Array<String>) {
    val jack = User(name = "Jack", age = 1)
    val olderJack = jack.copy(age = 2)//可以修改一个属性,也可以修改两个属性
    println(jack)
    println(olderJack)

}
输出结果为:
User(name=Jack, age=1)
User(name=Jack, age=2)

上面代码中修改属性的时候,属性的类型要和前面定义的一致

三.数据类以及解构声明

组件方法允许数据类在解构声明中使用:

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


fun main(args: Array<String>) { 

    val jane = User("Jane", 35)
    val (name, age) = jane  //Kotlin中居然可以这样声明和初始化属性!
    println("$name, $age years of age") // prints "Jane, 35 years of age"
}
打印结果:
Jane, 35 years of age

上面

val (name, age) = jane

其实就相当于

val name=jane.name
val age=jane.age

四.标准数据类

1.标准库提供了 Pair 和 Triple 。

在大多数情形中,命名数据类是更好的设计选择,因为这样代码可读性更强而且提供了有意义的名字和属性。

什么意思呢?
其实pair相关与java中的Map集合类,数据存储方式是key-value
Triple就不知道怎么说了,也是类似集合,可以三个数据一起那种的。

2.示例


fun main(args: Array<String>) {
    val a1=Pair(1,"a1")
    val a2=Pair(2,"a2")
    println(a1)
    println(a2)

    //Pair的类型可以不固定
    val map = mapOf(Pair(1, "A"), Pair("2", 500))
    for (entry in map) {
        println("${entry.key},${entry.value}")  //获取里面的key和value
        println("${entry.key.javaClass},${entry.value.javaClass}")//获取key、value对应的类型
    }

    val (i, j, k) = Triple(1, "a", 2.0)//可以存放不同类型的数据
    println("$i , $j , $k")   //获取数值
    println(i.javaClass.name+" , "+j.javaClass.name+" , "+k.javaClass.name)//获取数值的类型


    val v = Triple(1, "SB", 3) //只能三个
    println("${v.first},${v.second},${v.third}")

}

程序运行结果:

(1, a1)
(2, a2)
1,A
class java.lang.Integer,class java.lang.String
2,500
class java.lang.String,class java.lang.Integer
1 , a , 2.0
int , java.lang.String , double
1,SB,3

这个东西跟java相比也是一个奇葩。

五.密封类

1.密封类用来表示受限的类继承结构:当一个值为有限几种的类型, 而不能有任何其他类型时。

在某种意义上,他们是枚举类的扩展:枚举类型的值集合 也是受限的,
但每个枚举常量只存在一个实例,而密封类 的一个子类可以有可包含状态的多个实例。

声明一个密封类,使用 sealed 修饰类,密封类可以有子类,但是所有的子类都必须要内嵌在密封类中。

sealed 不能修饰 interface ,abstract class(会报 warning,但是不会出现编译错误)

sealed class Expr //定义一个密封类

data class Const(val number: Double) : Expr() //定义密封类的子类Const

data class Sum(val e1: Expr, val e2: Expr) : Expr() //定义密封类的子类Sum

object NotANumber : Expr() //???

fun eval(expr: Expr): Double = when (expr) {//返回一个浮点数的方法
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN
}

使用密封类的关键好处在于使用 when 表达式 的时候,如果能够验证语句覆盖了所有情况,就不需要为该语句再添加一个 else 子句了。

fun eval(expr: Expr): Double = when(expr) {
    is Expr.Const -> expr.number
    is Expr.Sum -> eval(expr.e1) + eval(expr.e2)
    Expr.NotANumber -> Double.NaN
    // 不再需要 `else` 子句,因为我们已经覆盖了所有的情况
}

看完上面Kotliin中,密封类,好像有点懂,但是不知道怎么运用,运行。

2.网上看了一个示例:

/**
 * 密封类
 * 1.密封类用sealed关键词表示
 * 2.密封类的子类只能定义在密封类的内部或同一个文件中,因为其构造方法为私有的
 * 3.密封类相比于普通的open类,可以不被此文件外被继承,有效保护代码
 * 4.与枚举的区别:密封类适用于子类可数的情况,枚举适用于实例可数的情况
 */


sealed class PlayerCmd { //演奏控制类(密封类)
    val playerName: String = "Player"

    //演奏类
    class Player(val url: String, val position: Long = 0): PlayerCmd() { 
        fun showUrl() {
            println("$url, $position")
        }
    }

    class Seek(val position: Long): PlayerCmd() //快进

    object Pause: PlayerCmd() //暂停(无需进行重载的类适合用单例object)
}

//(密封类的子类也可以定义在密封类的外部,但要在同一个文件中)
object Resume: PlayerCmd() //继续

object Stop: PlayerCmd() //停止

enum class PlayerState { //枚举适合表现简单的状态
    IDLE, PAUSE, PLAYING, STOP
}

fun main(args: Array<String>) {
    PlayerCmd.Player("苍茫的天涯").showUrl()
    println(Resume.playerName)
}


运行结果:

苍茫的天涯, 0
Player

看完上面这个示例,感觉更加懵逼了!

3.上网看了另一个简单的示例



sealed class Time

data class Time1(val number: Int) : Time() 

data class Time2(val number: Int, val name: String) : Time() 

 fun main(args: Array<String>) {
        println(list(Time1(5)))
        println(list(Time2(8,"hehe")))
}

 fun list(time:Time) : String = when(time){
       is Time1 -> "${time.number}"
       is Time2 -> time.name
   }

上面的代码是可以直接放到文件中测试运行的

运行结果:
5
hehe

看完这个示例,我就知道刚开始那个代码应该怎样运行了。

Kotlin中方法是可以不写在类里面的,main方法是肯定不写在类里面的!

4.密封类最开始的代码的示例


sealed class Expr //定义一个密封类

data class Const(val number: Double) : Expr() //定义密封类的子类Const

data class Sum(val e1: Expr, val e2: Expr) : Expr() //定义密封类的子类Sum

object NotANumber : Expr() //???,后面查了一下,其实object就像java中的static静态作用,但是用过不同,看下面调用吧

fun eval(expr: Expr): Double = when (expr) {//返回一个浮点数的方法
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN
}

 fun main(args: Array<String>) {
        println(eval(Const(5.0)))//5.0
        println(eval(Sum(Const(5.0),Const(15.0))))//5.0+15.0
        println(eval(NotANumber)) //因为NotANumber是静态的,不是方法
}

代码运行结果:

5.0
20.0
NaN

到这里Kotlin奇葩的数据类就介绍到这里了。
上面所有的示例,都是可以在线测试的,可以测试体验一把!
https://blog.csdn.net/wenzhi20102321/article/details/79859347

共勉:坚持做一件事情你就会有更多的时间和更多的精力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

峥嵘life

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值