83.Kotlin語言的繼承與重載的open關(guān)鍵字
84.Kotlin語言的類型轉(zhuǎn)換
85.Kotlin語言的智能類型轉(zhuǎn)換
86.Kotlin語言的Any超類
87.Kotlin語言的對象聲明
88.Kotlin語言的對象表達(dá)式
89.Kotlin語言的伴生對象
90.Kotlin語言的嵌套類
91.Kotlin語言的數(shù)據(jù)類
92.Kotlin語言的copy函數(shù)
93.Kotlin語言的解構(gòu)聲明
94-Kotlin語言的運算符重載
95-Kotlin語言的枚舉類
96-Kotlin語言的枚舉類定義函數(shù)
97-Kotlin語言的代數(shù)數(shù)據(jù)類型
98-Kotlin語言的密封類
99-數(shù)據(jù)類使用條件
open 聲明class, is 和 as 關(guān)鍵字 作為檢查和轉(zhuǎn)換操作
kotlin 默認(rèn)創(chuàng)建一個Class情況的class是底層:public final class 拒絕繼承
如果想要可以后續(xù)繼承使用,需要在前面添加open 關(guān)鍵字
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 關(guān)鍵字
安全非空操作
val x: String? = y as? String
Kotlin 在編譯時確保涉及泛型的操作的類型安全,而在運行時,泛型類型的實例不保存有關(guān)其實際類型參數(shù)的信息。
List<Foo> 被刪除為 List<*>。 通常,無法在運行時檢查實例是否屬于具有某些類型參數(shù)的泛型類型。
因此,編譯器禁止由于類型擦除而無法在運行時執(zhí)行的 is-check,例如 ints is List<Int> 或 list is T(類型參數(shù))。 可以根據(jù)星形投影類型檢查實例:【Any?】
koltlin 類型推斷,進(jìn)行了只能轉(zhuǎn)換 【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 內(nèi)聯(lián)函數(shù)一起用
上面說到open,和類型轉(zhuǎn)換提到了泛型轉(zhuǎn)換
【 Reified 允許您在使用泛型來進(jìn)行編程的同時,還能夠在運行時獲取到泛型所代表的類型信息,這在之前是無法做到的。當(dāng)您需要在內(nèi)聯(lián)函數(shù)中使用到類型信息,或者需要重載泛型返回值時,您可以使用 reified。使用 reified 不會帶來任何性能上的損失,但是如果被內(nèi)聯(lián)的函數(shù)過于復(fù)雜則,還是可能會導(dǎo)致性能問題。因為 reified 必須使用內(nèi)聯(lián)函數(shù),所以要保證內(nèi)聯(lián)函數(shù)的簡短,并且遵循使用內(nèi)聯(lián)函數(shù)的最佳實踐,以免讓性能受到損失。】
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中之制定了標(biāo)準(zhǔn),具體實現(xiàn)在各個平臺實現(xiàn)好了
單利實現(xiàn)
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 實現(xiàn)final 實現(xiàn),static 中給public static final Test1 INSTANCE 初始化
接口interface聲明調(diào)用,Kotlin只有一種方式object:接口實現(xiàn)。 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 具名實現(xiàn) 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("匿名實現(xiàn)")
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的方式
內(nèi)部類需要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() {
// 內(nèi)部類:
Test4("isOK").Hand().LeftHand().run()
Test4("isOK").Hand().RightHand().run()
// 嵌套類:
Outer.Nested().nest()
}
嵌套類特點:外部的類 能訪問 內(nèi)部的嵌套類,內(nèi)部的類 不能訪問 外部類的成員
內(nèi)部類的特點: 內(nèi)部的類 能訪問 外部的類,外部的類 能訪問 內(nèi)部的類
data class 數(shù)據(jù)類和普通類
data class 特點:
- 內(nèi)部進(jìn)行了component 函數(shù)對字段進(jìn)行解構(gòu)
- toString復(fù)寫
- copy 克隆函數(shù)
- 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,只關(guān)心主構(gòu)造,不管次構(gòu)造
data class DataPerson(var name:String,var age:Int){
var corinfo = ""
init {
println("主構(gòu)造被調(diào)用")
}
constructor(name:String):this(name,12){
println("此構(gòu)造constructer構(gòu)造被調(diào)用")
corinfo = "增加核心內(nèi)容"
}
override fun toString(): String {
return "toString name:$name, age:$age, coreInfo:$corinfo"
}
}
調(diào)用:
val p1 = DataPerson("zhangsan")
val p2 = p1.copy("zcwfeng",100)
println(p1)
println(p2)
--------
主構(gòu)造被調(diào)用
此構(gòu)造constructer構(gòu)造被調(diào)用
主構(gòu)造被調(diào)用
toString name:zhangsan, age:12, coreInfo:增加核心內(nèi)容
toString name:zcwfeng, age:100, coreInfo:
data class 解構(gòu)和普通class結(jié)構(gòu)對比
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("解構(gòu):name=$name,age=$age")
val (name1,age1) = Student2Data("test",1111)
println("解構(gòu)2:name=$name1,age=$age1")
}
必須加入操作符標(biāo)記,否則復(fù)發(fā)接受自定義結(jié)構(gòu) operator fun component1():String = name
目的對比data class 解釋解構(gòu)的特性
操作符重栽,聯(lián)想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也是可以的
枚舉類的使用和經(jīng)驗
簡單項目中狀態(tài)
enum class UIStateEnum constructor(v: Int) {
//1,取消搜藏,2,搜藏,3,取消點贊,4,點贊,5 取消關(guān)注,6 關(guān)注,7,取消關(guān)注番劇 8,關(guān)注追番劇
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("更新前的數(shù)據(jù)是:${this.info}")
this.info.libinfo = info.libinfo
this.info.length = info.length
println("更新后的數(shù)據(jù)是:${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 使用
舉個我們九年義務(wù)教育和各種教育都用的例子。考試黨委,我們只輸出優(yōu)秀學(xué)生的名字。
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 -> "優(yōu)秀,學(xué)生名字是${(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
}
這個有點想我們的枚舉,使用枚舉可以但是比較麻煩和難做。相似點都是代數(shù)數(shù)據(jù)模型。
解釋一下
1 限制枚舉每個類型只允許有一個實例
2 限制所有枚舉常量使用相同的類型的值
3 Sealed Classes 包含了抽象類和枚舉的優(yōu)勢:抽象類表示的靈活性和枚舉常量的受限性
4 Sealed Classes 用于表示受限制的類層次結(jié)構(gòu),從某種意義上說,Sealed Classes 是枚舉類的擴(kuò)展
5 枚舉的不同之處在于,每個枚舉常量僅作為單個實例存在,而 Sealed Classes 的子類可以表示不同狀態(tài)的實例
Sealed Classes 用于表示層級關(guān)系-->子類可以是任意的類, 數(shù)據(jù)類、Kotlin 對象、普通的類,甚至也可以是另一個 Sealed
Sealed Classes 受限制----> 必須在同一文件中,或者在 Sealed Classes 類的內(nèi)部中使用,在Kotlin 1.1 之前,規(guī)則更加嚴(yán)格,子類只能在 Sealed Classes 類的內(nèi)部中使用
這里說的不是十分準(zhǔn)確,能夠解釋為什么用Sealed比枚舉合適
data class使用場景
- 數(shù)據(jù)請求返回bean。
- 不允許sealed,abstract,inner,open的修飾
- 至少有一個主構(gòu)造參數(shù),并且必須用var,val修飾的
- 需要比較,copy,解構(gòu),toString方便的時候可以使用