三、数据类
使用 data class 关键字创建一个只包含数据的类,这种类又称数据类。这个是Kotlin独有的,Java没有数据类。
编译器会自动的从主构造函数中根据所有声明的属性提取以下函数:
- 生成 equals() 函数与 hasCode() 函数
- 生成 toString() 函数,由类名(参数1 = 值1,参数2 = 值2,….) 构成
- 由所定义的属性自动生成component1()、component2()、…、componentN()函数,其对应于属性的声明顺序。
- copy() 函数
数据类需要满足以下条件:
- 主构造函数至少包含一个参数。
- 所有的主构造函数的参数必须标识为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