数据类(Data Class)
我们经常会频繁的创建保存数据的类。在这样的一个类中,一些标准的函数是可以从data中导出的。在kotlin中,这被称为data class。
data class User(val name : String , age : Int)
这个编译器会自动从primary constructor的声明属性中得到下面成员:
- equals()/hashCode 对
- toString() 以格式“User(name = john, age = 42)”
- 与属性声明顺序相关的 componetN()方法
- copy()方法
为了确保data class 生成代码的一致性,必须要保证它满足一下特点:
- primary construcotr 的参数个数必须大于0
- primary constructor中的参数必须标明“val”或者“var”
- data class 不能为抽象,open,sealed ,或者inner
- 1.1之前,data class只能实现一个接口
另外,成员的生成遵循一下原则,考虑到成员的继承。
* 如果data class中显示实现了 equal(),hashCode()或者toString()时,这些方法将不会被生成,自定义实现的将被使用。
* 如果父类有componentN()方法,并且它是open 且返回值类型兼容,那么子类相应的函数将会重写父类。如果由于签名类型或者final导致父类方法不会重写,将会报出异常。
* 继承一个有copy方法的父类将被禁止(版本1.2过时,版本1.3禁止)
* 提供显式的componentN()/copy()将不被允许。
从1.1开始 data class 可以继承其它类。
在JVM中,如果一个类需要一个无参构造函数,默认值将被指明到相应属性。
data class User(val name : String = "" , val age : Int = 0)
类体中声明的属性
注意,编译器仅仅使用primary constructor中定义的参数自动生成方法。为了排除一些属性,将它声明在类体。
data class User(val name : String){
var age : Int = 0
}
仅有name属性将被在 toString()、hashCode()、equals() 和copy()方法实现中。并且仅有一个名为component1()的componentN()方法。如下面案例,尽管两个User object 有不同的年龄,但是它们被认为是 euqals。
User user1 = User("android")
User user2 = User("android")
user1.age = 10
user2.age = 20
复制ing
我们经常遇到这样一种情况:我们需要复制一个对象,但是要改变其中的部分属性。这就是需要copy() 来满足需求。
上面类中的copy方法实现如下:
fun copy(name : String = this.name , age : Int = this.age) = User(name, age)
下面的调用满足上面的需求
val jack = User(name = "jack" , age = 1)
val oderJack = jack.copy(age = 2)
数据类和解构声明
详情查询解构声明
val jane = User("jane",35)
val (name,age) = jame
println("$name , $age years of age") //prints "Jane , 35 years of age"
标准的数据类
标准库提供了Pair和Triple。在大多是情况下命名一个数据类是一个更好的设计选择。因为它增加代码的可读性,通过提供有意义的名字
封装类(密封类)
密封类被用于限制类的继承,当一个值只有一种类型,不能有其它种类型。从某种意义上讲,它是枚举类型的扩展。一个枚举的值是被限制的,但是每一个枚举常量作为一个实例存在,而一个密封类的子类可以有多个实例保存状态。
为了声明一个密封类,你需要加sealed关键字。一个密封类可以有子类,但是这些子类必须和密封类房子同一个文件中。在1.1之前还必须嵌套。
sealed class Expr
data class Const(val number : Double) : Expr()
data class Sum(val e1 : Expr,val e2 : Expr):Expr()
object NotANumber : Expr()
上面的例子中,包含了版本1.1的一个新特性 允许data class 继承 其他class。
一个密封类可以抽象的,但是不能被直接实例化。
一个密封类不允许有非私有构造函数,它们的构造函数默认是私有的
注意继承密封类的子类的子类可以在任何地方进行声明。
使用密封类的优点在于引入了when表达式。
fun eval( expr :Expr) : Double = when(expr){
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
}