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