83.Kotlin语言的继承与重载的open关键字
84.Kotlin语言的类型转换
85.Kotlin语言的智能类型转换
86.Kotlin语言的Any超类
87.Kotlin语言的对象声明
88.Kotlin语言的对象表达式
89.Kotlin语言的伴生对象
90.Kotlin语言的嵌套类
91.Kotlin语言的数据类
92.Kotlin语言的copy函数
93.Kotlin语言的解构声明
94-Kotlin语言的运算符重载
95-Kotlin语言的枚举类
96-Kotlin语言的枚举类定义函数
97-Kotlin语言的代数数据类型
98-Kotlin语言的密封类
99-数据类使用条件
open 声明class, is 和 as 关键字 作为检查和转换操作
kotlin 默认创建一个Class情况的class是底层:public final class 拒绝继承
如果想要可以后续继承使用,需要在前面添加open 关键字
open class Parent(var name:String){
fun show() = println("$name")
}
class Children(var childName:String):Parent(childName){
fun show2() {
println("parent:$name,child:$childName")
}
}
fun main() {
val p:Parent = Children("zcwfeng")
println(p.name)
val c = Children("david")
if(c is Parent){
val c2:Parent = c as Parent
println(c2.show())
}
}
is 和 as 关键字
安全非空操作
val x: String? = y as? String
Kotlin 在编译时确保涉及泛型的操作的类型安全,而在运行时,泛型类型的实例不保存有关其实际类型参数的信息。
List<Foo> 被删除为 List<*>。 通常,无法在运行时检查实例是否属于具有某些类型参数的泛型类型。
因此,编译器禁止由于类型擦除而无法在运行时执行的 is-check,例如 ints is List<Int> 或 list is T(类型参数)。 可以根据星形投影类型检查实例:【Any?】
koltlin 类型推断,进行了只能转换 【handleStrings】
if (something is List<*>) {
something.forEach { println(it) } // The items are typed as `Any?`
}
val something = listOf("a","b")
if(something is List<*>){
println(something)
}
fun handleStrings(list: List<String>) {
if (list is ArrayList) {
// `list` is smart-cast to `ArrayList<String>`
}
}
Reified 需要和inline 内联函数一起用
上面说到open,和类型转换提到了泛型转换
【 Reified 允许您在使用泛型来进行编程的同时,还能够在运行时获取到泛型所代表的类型信息,这在之前是无法做到的。当您需要在内联函数中使用到类型信息,或者需要重载泛型返回值时,您可以使用 reified。使用 reified 不会带来任何性能上的损失,但是如果被内联的函数过于复杂则,还是可能会导致性能问题。因为 reified 必须使用内联函数,所以要保证内联函数的简短,并且遵循使用内联函数的最佳实践,以免让性能受到损失。】
inline fun <reified A, reified B> Pair<*, *>.asPairOf(): Pair<A, B>? {
if (first !is A || second !is B) return null
return first as A to second as B
}
val somePair: Pair<Any?, Any?> = "items" to listOf(1, 2, 3)
val stringToSomething = somePair.asPairOf<String, Any>()
val stringToInt = somePair.asPairOf<String, Int>()
val stringToList = somePair.asPairOf<String, List<*>>()
val stringToStringList = somePair.asPairOf<String, List<String>>()
println("somePair -> ${somePair}")
println("stringToSomething -> ${stringToSomething}")
println("stringToInt -> ${stringToInt}")
println("stringToList -> ${stringToList}")
println("stringToStringList -> ${stringToStringList}")
Kt 所有类都隐含继承了Any(),Any类在Kt中之制定了标准,具体实现在各个平台实现好了
单利实现
object class
object Test1 {
init {
println("kt init")
}
fun show() = println("ia am show Object class")
}
fun main() {
println(Test1)
println(Test1)
println(Test1)
println(Test1.show())
}
原理可以借助编译器 中kt的反编译工具查看。init----》static代码块,show 实现final 实现,static 中给public static final Test1 INSTANCE
初始化
接口interface声明调用,Kotlin只有一种方式object:接口实现。 java 有两种方式
open class Test2 {
open fun add(info:String) = println("kt add:$info")
}
class TestImpl:Test2(){
override fun add(info: String) {
// super.add(info)
println("kt 具名实现 add:$info")
}
}
interface RunnableKt{
fun runKt()
}
fun main() {
val p = object : Test2(){
override fun add(info: String) {
// super.add(info)
println("kt 匿名 add:$info")
}
}
p.add("匿名实现")
val pImpl = TestImpl()
pImpl.add("张三")
//Java interface 方式
val p2 = object:Runnable{
override fun run() {
println("Java interface")
}
}
val p3 = Runnable { println("Java interface2") }
p2.run()
p3.run()
//Kt Interface 只有一种
object:RunnableKt{
override fun runKt() {
println("Kt interface")
}
}.runKt()
}
伴生生对象,这个点很简单,Companion看反编译代码
class Test3 {
companion object{
val info = "demo"
fun show() = println("compaion $info")
}
}
fun main() {
println(Test3.info)
println(Test3.show())
//companion 只有一次初始化
Test3()
Test3()
Test3()
}
companion 底层实际,static final class的方式
内部类需要inner修饰,嵌套类
class Test4(body:String) {
val bodyInfo = body
inner class Hand{
fun work() = println("Hand is work with: $bodyInfo")
inner class LeftHand { // 左手
fun run() = println("左手访问身体信息:$bodyInfo")
}
inner class RightHand { // 右手
fun run() = println("右手访问身体信息:$bodyInfo")
}
}
inner class Heart{
fun work() = println("Hand is work with: $bodyInfo")
}
fun show(){
Hand().work()
}
}
class Outer{
val outString = "aaa"
fun out(){
Nested().nest()
}
class Nested{
fun nest(){
println("nest class")
// Outer().out()
// out()
// println(outString)
}
}
}
fun main() {
// 内部类:
Test4("isOK").Hand().LeftHand().run()
Test4("isOK").Hand().RightHand().run()
// 嵌套类:
Outer.Nested().nest()
}
嵌套类特点:外部的类 能访问 内部的嵌套类,内部的类 不能访问 外部类的成员
内部类的特点: 内部的类 能访问 外部的类,外部的类 能访问 内部的类
data class 数据类和普通类
data class 特点:
- 内部进行了component 函数对字段进行解构
- toString复写
- copy 克隆函数
- equals 重写
class Normal(var code:Int,var msg:String,var data:String)
data class DataNormal(var code:Int,var msg:String,var data:String)
fun main() {
println(Normal(200,"isOk","body"))
println(DataNormal(200,"isOk","body"))
println(Normal(200,"isOk","body") == Normal(200,"isOk","body"))//false
println(DataNormal(200,"isOk","body") == DataNormal(200,"isOk","body"))//true
}
data 的copy,只关心主构造,不管次构造
data class DataPerson(var name:String,var age:Int){
var corinfo = ""
init {
println("主构造被调用")
}
constructor(name:String):this(name,12){
println("此构造constructer构造被调用")
corinfo = "增加核心内容"
}
override fun toString(): String {
return "toString name:$name, age:$age, coreInfo:$corinfo"
}
}
调用:
val p1 = DataPerson("zhangsan")
val p2 = p1.copy("zcwfeng",100)
println(p1)
println(p2)
--------
主构造被调用
此构造constructer构造被调用
主构造被调用
toString name:zhangsan, age:12, coreInfo:增加核心内容
toString name:zcwfeng, age:100, coreInfo:
data class 解构和普通class结构对比
class Student(var name:String,var age:Int){
operator fun component1():String = name
operator fun component2():Int = age
}
data class Student2Data(var name:String,var age:Int)
fun main() {
val (name,age) = Student("test",1111)
println("解构:name=$name,age=$age")
val (name1,age1) = Student2Data("test",1111)
println("解构2:name=$name1,age=$age1")
}
必须加入操作符标记,否则复发接受自定义结构 operator fun component1():String = name
目的对比data class 解释解构的特性
操作符重栽,联想c++
data class AddPlus(var num1:Int,var num2:Int){
operator fun plus(p:AddPlus): Int {
return (p.num1 +num1 + p.num2 + num2)
}
//AddPlus. 可以提示你可重新载入的操作符
operator fun div(p:AddPlus):AddPlus{
return AddPlus(num1+p.num1,num2 + p.num2)
}
}
fun main() {
println(AddPlus(1,1) + AddPlus(2,2))
// val (a,b) = AddPlus(1,1) / AddPlus(2,2)
// println("a,b,$a,$b")
println(AddPlus(1,1) / AddPlus(2,2))
}
利用编译器提示可用的操作符,AddPlus 加点就可以
用data class是为了输出方便查看,直接class也是可以的
枚举类的使用和经验
简单项目中状态
enum class UIStateEnum constructor(v: Int) {
//1,取消搜藏,2,搜藏,3,取消点赞,4,点赞,5 取消关注,6 关注,7,取消关注番剧 8,关注追番剧
Undefined(0),
CancelCollect(1),
CollectState(2),
UnLikeState(3),
LikeState(4),
UnFollowUser(5),
FollowUser(6),
UnFollowVideo(7),
FollowVideo(8);
var value = v
override fun toString(): String {
return "$value"
}
}
println(UIStateEnum.Undefined.value)
println(UIStateEnum.Undefined.toString())
简单封装一下使用
data class Info(var libinfo:String,var length:Int)
enum class LibInfo(var info: Info){
L_HAND(Info("左手", 88)), // 左手
R_HAND(Info("右手", 88)), // 右手
L_FOOT(Info("左脚", 140)), // 左脚
R_FOOT(Info("右脚", 140)) // 右脚
;
fun show() = "四肢是:${info.libinfo}的长度是:${info.length}"
fun updateData(info: Info) {
println("更新前的数据是:${this.info}")
this.info.libinfo = info.libinfo
this.info.length = info.length
println("更新后的数据是:${this.info}")
}
}
// 一般的用法如下:
println(LibInfo.L_HAND.show())
println(LibInfo.R_HAND.show())
println(LibInfo.L_FOOT.show())
println(LibInfo.R_FOOT.show())
println()
// 更新枚举值
LibInfo.R_HAND.updateData(Info("====右手22222222", 11))
LibInfo.L_HAND.updateData(Info("====左手22222222", 11))
LibInfo.L_FOOT.updateData(Info("====左脚222222", 199))
LibInfo.R_FOOT.updateData(Info("====右叫222222", 199))
密封类sealed class 使用
举个我们九年义务教育和各种教育都用的例子。考试党委,我们只输出优秀学生的名字。
sealed class Exams{
object Fun1:Exams()
object Fun2:Exams()
class Fun3(val name:String):Exams()
}
class Teachers(var exam:Exams){
fun show() =
when (exam) {
is Exams.Fun1 -> "及格"
is Exams.Fun2 -> "良好"
is Exams.Fun3 -> "优秀,学生名字是${(this.exam as Exams.Fun3).name}"
}
}
fun main() {
println(Teachers(Exams.Fun1).show())
println(Teachers(Exams.Fun2).show())
println(Teachers(Exams.Fun3("zhang san")).show())
println(Teachers(Exams.Fun3("wang wu")).show())
println(Exams.Fun1 === Exams.Fun1) // true, === 必须对象引用, object是单例 只会实例化一次
println(Exams.Fun3("AAA") === Exams.Fun3("AAA")) // class 有两个不同的对象,所以是false
}
这个有点想我们的枚举,使用枚举可以但是比较麻烦和难做。相似点都是代数数据模型。
解释一下
1 限制枚举每个类型只允许有一个实例
2 限制所有枚举常量使用相同的类型的值
3 Sealed Classes 包含了抽象类和枚举的优势:抽象类表示的灵活性和枚举常量的受限性
4 Sealed Classes 用于表示受限制的类层次结构,从某种意义上说,Sealed Classes 是枚举类的扩展
5 枚举的不同之处在于,每个枚举常量仅作为单个实例存在,而 Sealed Classes 的子类可以表示不同状态的实例
Sealed Classes 用于表示层级关系-->子类可以是任意的类, 数据类、Kotlin 对象、普通的类,甚至也可以是另一个 Sealed
Sealed Classes 受限制----> 必须在同一文件中,或者在 Sealed Classes 类的内部中使用,在Kotlin 1.1 之前,规则更加严格,子类只能在 Sealed Classes 类的内部中使用
这里说的不是十分准确,能够解释为什么用Sealed比枚举合适
data class使用场景
- 数据请求返回bean。
- 不允许sealed,abstract,inner,open的修饰
- 至少有一个主构造参数,并且必须用var,val修饰的
- 需要比较,copy,解构,toString方便的时候可以使用