三、数据类
使用 data class 关键字创建一个只包含数据的类,这种类又称数据类。这个是Kotlin独有的,Java没有数据类。
编译器会自动的从主构造函数中根据所有声明的属性提取以下函数:
- 生成 equals() 函数与 hasCode() 函数
- 生成 toString() 函数,由类名(参数1 = 值1,参数2 = 值2,….) 构成
- 由所定义的属性自动生成component1()、component2()、…、componentN()函数,其对应于属性的声明顺序。
数据类需要满足以下条件:
- 所有的主构造函数的参数必须标识为val或者var。
- 数据类不可以声明为abstract,open,sealed或者inner。
data class DaTang (var name:String ,val age:Int)
在没有结构体的时候,大括号{}可省略。
3.1 创建数据类
data class DaTang (var name:String ,val age:Int){
val emperor = "$name,是继隋朝之后的大一统中原王朝,共历二十一帝,享国-$age-年。"
}
fun main() {
println(DaTang("唐朝", 289))
}

如果不使用 data class 关键字修饰,而使用class关键字修饰,如下:

3.2 toString、equals和hashCode的个性化实现

3.3 ==符号
data class DaTang (var name:String ,val age:Int){
val emperor = "$name,是继隋朝之后的大一统中原王朝,共历二十一帝,享国-$age-年。"
}
class DaTang2 (var name:String ,val age:Int){
val emperor = "$name,是继隋朝之后的大一统中原王朝,共历二十一帝,享国-$age-年。"
}
fun main() {
println(DaTang("唐朝", 289))
// "==“比较的是内容 equals(Any)。
// 因未重写Any的equals函数,使用的是Any默认equals函数,所以比较的还是引用。
// "===" 比较的是引用(类所占的内存区域)
println(DaTang2("唐朝", 289) == DaTang2("唐朝", 289))
//这里使用的是data class,数据类重写了equals,比较的是数据类里面的数据。
println(DaTang("唐朝", 289) == DaTang("唐朝", 289))
}

3.4 copy() 函数
copy() 函数应该是类似Java中的clone() 方法
data class DaTang (var name:String ,val age:Int){
val emperor = "$name,是继隋朝之后的大一统中原王朝,共历二十一帝,享国-$age-年。"
}
fun main() {
val datang = DaTang("唐朝", 289)
println(datang)//DaTang(name=唐朝, age=289)
//创建了了一个新的对象
val diguo = datang.copy(age=500)
println(diguo)//DaTang(name=唐朝, age=500)
}

3.5 解构声明
数据类:由所定义的属性自动生成component1()、component2()、…、componentN()函数,其对应于属性的声明顺序。
普通类:使用 operator 关键字定义component1()、component2()、…、componentN()函数。
data class DataDaSong(var name:String ,var age:Int)
class DaSong(var name:String ,var age:Int){
//解构语法:必须从component1开始
operator fun component1() = name
operator fun component2() = age
}
fun main() {
//使用普通类需要自己写component1、component2...componentN
var (name,age) = DaSong("北宋",167)
println("$name,是中国历史上继五代十国之后的朝代,传九位皇帝,享国-$age-年")
//使用数据类支持解构语法,自动生成operator fun component1
var (dataname,dataage) = DataDaSong("北宋",167)
println("数据类:$dataname,是中国历史上继五代十国之后的朝代,传九位皇帝,享国-$dataage-年")
}

查看数据类反编译代码:

四、 继承(extend)
Kotlin 允许一个类继承自另一个类,Kotlin 中所有类都继承自 Any 类,Any 类是所有类的超类,对于没有超类型声明的类是默认超类
Kotlin 类默认都是封闭的,要让某个类开放继承,必须使用 open 关键字修饰它。
注意:
在 Kotlin 中 Any 类是所有类的超类
在 Java 中 Object 类是所有类的超类
4.1 Any 超类
Any 默认提供了三个函数:
public open class Any {
public open operator fun equals(other: Any?): Boolean
public open fun hashCode(): Int
public open fun toString(): String
}
从这里发现 无论是类还是函数,都使用了 open 关键字修饰。
同时发现,这里只是定义了函数,没有实现。为什么呢?因为Kotlin是跨平台语言,可以在Android、macOS、Windows、JavaScript上运行,为了支持跨平台更友好,在不同平台有不同实现,所以在这里并未展示。
4.2 继承类
/* 食物基类 */
open class Food{
fun explain() = "Food explain"
}
class Apple :Food(){
}
不加 open 关键字修饰是不让继承滴,如下图:

4.3 函数重写
在基类中,使用 fun 声明函数时,此函数默认为 final 修饰,不能被子类重写。
如果允许子类重写该函数,那么必须使用 open 关键字修饰它, 子类重写函数使用 override 关键字。
/* 食物基类 */
open class Food{
//函数必须用 open 关键字修饰,子类才能覆盖
open fun explain() = "Food explain"
}
/* 继承Food */
class Apple :Food(){
//使用 override 关键字覆盖父类函数
override fun explain() = "Apple explain "
}
在 Kotlin 中 override 是个关键字而不是注解。
使用
fun main() {
//多态:父类类型的引用子类类型的对象
val f: Food = Apple()
println(f.explain())
}

4.4 属性重写
/* 食物基类 */
open class Food {
open val price = 100
...
}
/* 继承Food */
class Apple : Food() {
override var price = 36
...
}
fun main() {
//多态:父类类型的引用子类类型的对象
val f: Food = Apple()
println(f.explain())//Apple explain 36
}

4.5 类型检测(is
)
Kotlin的 is运算符 可以用来检查某个对象的类型。
println(f is Food)//true
println(f is Apple)//true
println(f is File)//false
智能类型转换(as
)
Kotlin的 as运算符 可以用对某个对象进行类型转换。

智能安全转换操作符:as?
as?安全地转换成一种类型。 如果无法进行转换,则返回null,而不是抛出ClassCastException异常。
咱就在上面的实例基础上修改。
var asTest :String? = ""
//不安全的转换操作符 as
// println(asTest as Int)//ClassCastException
//安全的转换操作符 as?
println(asTest as? Int)//null
