1. 程式人生 > >Kotlin實戰(五)

Kotlin實戰(五)

一、面向物件基礎知識

1.1、面向物件案例

Kotlin的類申明,預設是final和public的,Kotlin類、介面修飾符:

  • public:預設修飾符,被其修飾的在任何位置都能訪問
  • private:表示只在這個類(以及它的所有成員)之內可以訪問
  • protected:在當前類及其子類內訪問
  • internal:在同一模組內使用
val a = Rect()
println(a.height)//100
println(a.width)//100
val b = Girl()
println(b.name)//張三
println(b.age)//18
b.shopping
()//去購物 // 矩形 class Rect { //靜態屬性 var height: Int = 100 var width: Int = 100 } // 妹子 class Girl { //靜態屬性 var name: String = "張三" var age: Int = 18 //動態行為 fun shopping() { println("去購物") } }

1.2、get和set

val修飾的變數是沒有set方法的, 也不允許重寫set方法。

fun main(args: Array<String>
) { val person = Person() //person.age=20 //val修飾的變數是沒有set方法的, 也不允許重寫set方法 println("姓名:${person.name},年齡:${person.age}")//姓名:張三,年齡:18 } //koltin欄位是私有的 會自動生成get和set方法 可以直接訪問 class Person{ var name:String="張三" val age:Int=18 //val修飾的變數是沒有set方法的, 也不允許重寫set方法 }

1.3、訪問器

fun main(args: Array<
String>) { val person = Person2() // person.name="11"//私有了set方法 不可修改 println("姓名:${person.name},年齡:${person.age}")//姓名:張三,年齡:18 } //private set 私有了set方法 只能訪問 不可修改 class Person2 { var name: String = "張三" private set //私有了set方法 var age: Int = 18 }

1.4、自定義訪問器

   fun main(args: Array<String>) {
    val person = Person3()
    person.age=120
    println("姓名:${person.name},年齡:${person.age}")//姓名:張三,年齡:120
    person.age=160
    println("姓名:${person.name},年齡:${person.age}")//姓名:張三,年齡:120
}
class Person3 {
    var name: String = "張三"
        private set  //私有了set方法
    
    var age: Int = 18
        //自定義訪問器
        set(value) {
            if (value < 150) {
                //使用field 修改 age  此時的field相當於this.age
                field = value
            }
        }
} 

field的用法,field被大神們翻譯成Backing Fields(後端變數),它的作用就類似於java裡的this.屬性名,例如上面程式碼中field = value, 就相當於java裡的 this.age = value,但是不能直接使用this.age,會造成遞迴呼叫記憶體溢位的, 因為在set和get中是不允許有本身的區域性變數的(例如如果你屬性名是age, 在set/get裡是不能有這個變數的), 因為屬性的呼叫也涉及到了set/get會造成遞迴呼叫, 所以要解決引用自身的問題, kotlin發明了field(後端變數)來解決這個問題。注意不是set/get裡不允許有區域性變數,是不允許有和屬性本身相同名字的區域性變數。

1.5、建構函式

Kotlin的建構函式,分為主建構函式和次建構函式。

fun main(args: Array<String>) {
    val person4 = Person4("張三", 18)
}
//建立的時候就需要修改裡面的name和age
//class Person4 constructor(name: String, age: Int) {
//}
//主建構函式沒有註解或可見性修飾符,constructor可去除
class Person4 (name: String, age: Int) {
}

1.6、建構函式引數的使用

因為kotlin中的類定義同時也是建構函式,這個時候是不能進行操作的,所以kotlin增加了一個新的關鍵字init用來處理類的初始化問題,init模組中的內容可以直接使用建構函式的引數。

fun main(args: Array<String>) {
    val person5 = Person5("張三", 99)
    println("姓名:${person5.name},年齡:${person5.age}")//姓名:張三,年齡:99
}

//建立的時候就需要修改裡面的name和age
class Person5(name: String, age: Int) {
    var name:String?=null
    var age:Int=0
    init {
        //建構函式中寫的程式碼可以放到init中執行
        this.name=name
        this.age=age
    }
}

1.7、建構函式引數使用var和val

沒有使用varval宣告的主建構函式需要手動賦值才可使用

//var add = Add("11", "22")
//println(add.value2)//訪問報錯
//class Add(value1: String,  value2: String)

var add = Add("11", "22")
println(add.value2)//22
class Add(value1: String,  value2: String){
    var value1:String
    var value2:String
    init {
        this.value1=value1
        this.value2=value2
    }
}

使用了varval宣告的主建構函式中引數,外部可以直接通過 物件.引數 來訪問

fun main(args: Array<String>) {
    val person6 = Person6("張三", 99)
//    person6.age=1//不可修改
    println("姓名:${person6.name},年齡:${person6.age}")//姓名:張三,年齡:99
    val person7 = Person7("張三", 91)
    person7.age=1//可修改
    println("姓名:${person7.name},年齡:${person7.age}")//姓名:張三,年齡:1
}

//建立的時候就需要修改裡面的name和age, val 相當於final 修飾  不可修改屬性
class Person6(val name: String, val age: Int)

//建立的時候就需要修改裡面的name和age,var 可修改屬性
class Person7(var name: String, var age: Int)

1.8、次構函式

次級建構函式用constructor加上引數,後面用this賦值給主建構函式的引數,同時次級建構函式中可以進行程式碼操作,所以沒有init模組

fun main(args: Array<String>) {
    val per = Per("張三", 18, 1)
    println("姓名:${per.name},年齡:${per.age},性別:${if (per.sax == 1) "" else ""}")//姓名:張三,年齡:18,性別:男
    val per2 = Per("李四", 18)
    println("姓名:${per2.name},年齡:${per2.age},性別:${if (per2.sax == 1) "" else ""}")//姓名:李四,年齡:18,性別:女
}

class Per(var name: String, var age: Int) {
    var sax: Int = 0
    //constructor次構函式 :this 賦值給主建構函式的引數
    constructor(name: String, age: Int, sax: Int) : this(name, age) {
        this.sax = sax
    }
}

1.9、init和次構執行順序

類被初始化時,先執行 init 方法後執行主建構函式方法。

fun main(args: Array<String>) {
    val per2 = Per2("張三", 18, 1)
    //列印結果:呼叫了init方法  呼叫了constructor方法
}

class Per2(name: String, age: Int) {
    var name: String = ""
    var age: Int = 0
    var sax: Int = 0

    init {
        println("呼叫了init方法")
        this.name = name
        this.age = age
    }

    constructor(name: String, age: Int, sax: Int) : this(name, age) {
        println("呼叫了constructor方法")
        this.sax = sax
    }
}

二、封裝

封裝:將相應的屬性、行為裝到一個類中,然後將所有屬性私有化。

fun main(args: Array<String>) {
    //買一個洗衣機
    val machine = WashMachine("海爾",12)
    //開啟洗衣機門
    machine.openDoor()
    //放入衣服
    println("放入牛仔褲")
    //關閉洗洗衣機門
    machine.closeDoor()
    //設定模式
    machine.mode = 1
    //點選開始洗衣服按鈕
    machine.start()
//    machine.setMotorSpeed(10000)//轉速調節歸洗衣機自生所有,不提供給外部呼叫
}

// 洗衣機
class  WashMachine(var brand: String, var l: Int) {
    var mode= 0//預設模式 1 輕柔  2 狂揉

    //開門動作
    fun openDoor(){
        println("開啟洗衣機門...")
    }
    //關閉洗衣機門
    fun closeDoor() {
        println("關閉洗衣機門...")
    }

    //調節轉速操作(不提供給使用者,使用private私有化)
    private fun setMotorSpeed(speed:Int){
        println("當前轉速為$speed")
    }

    //設定模式(使用者調節模式)
    fun selectMode(mode: Int) {
        this.mode = mode
    }

    //開始洗衣服的按鈕
    fun start() {
        when (mode) {
            0 -> {
                println("請選擇模式")
            }
            1 -> {
                println("開始放水...")
                println("水放滿了...")
                println("開始洗衣服...")
                println("洗衣服模式為輕柔")
                setMotorSpeed(500)
                println("衣服洗好了...")
            }
            2 -> {
                println("開始放水...")
                println("水放滿了...")
                println("開始洗衣服...")
                println("洗衣服模式為狂柔")
                setMotorSpeed(10000)
                println("衣服洗好了...")
            }
            else->{
                println("模式設定錯誤")
            }
        }
    }
}

三、繼承

Kotlin中使用 : 來繼承 ,一個類可繼承只可繼承一個類(單繼承),Java中類和方法預設是open的,而Kotlin中預設都是final的,類和方法可以被繼承需要open關鍵字來修飾,繼承方法必須使用override標識。 否則和父類可以被繼承的方法同名,編譯器將報錯。Kotlin中使用override修飾符是強制要求的,這樣會避免先寫出實現方法再新增抽象方法造成的意外重寫。

Kotlin中所有類都繼承Any 預設提供equals()hashCode()toString()

fun main(args: Array<String>) {
    val son = Son()
    son.horbe()
}

/**
 * 父親類
 * Kotlin中 預設類都是final 的不可繼承,需要增加open關鍵字  才可以繼承
 */
open class Father {
    //靜態屬性
    open var name: String = "張三"
    open var age: Int = 45
    //動態行為
    open fun horbe() {
        println("父親喜歡抽菸")
    }
}

//孩子類  繼承自父親
class Son : Father() {
    //繼承父親屬性
    override var name: String="張四"
    override var age: Int=1
    override fun horbe() {
//        super.horbe()
        println("孩子喜歡抽菸")
    }
}

四、抽象類

使用abstract來修飾表示抽象類,kotlin中方法和屬性都可以為抽象。

fun main(args: Array<String>) {
    val zhHuman = ZHHuman()
    val color = zhHuman.color
    val language = zhHuman.language
    println("面板顏色:$color,使用語言:$language")//面板顏色:黃色,使用語言:中文

}

//人類(抽象類)
abstract class Human {
    //膚色屬性
    abstract var color: String
    //語言屬性
    abstract var language: String

    //吃飯行為
    abstract fun eat()
}

//中國人(繼承人類抽象類) open 用於其他類繼承
open class ZHHuman : Human() {
     override var color: String = "黃色"
     override var language: String = "中文"
     override fun eat() {
        println("使用筷子吃飯")
    }
}
//美國人(繼承人類抽象類)
class USHuman : Human() {
    override var color: String = "白色"
    override var language: String = "英文"
    override fun eat() {
        println("使用刀叉吃飯")
    }
}

五、介面

5.1、介面定義

Kotlin中使用 : 來實現介面,多個介面使用,隔開,一個類可實現多個介面(多實現),實現介面中方法必須使用override標識, 否則編譯器將報錯。

fun main(args: Array<String>) {
    val xiaoMing = XiaoMing()
    xiaoMing.ride()//小明學會了騎自行車
    xiaoMing.drive()//小明學會了開車
}

//小明 繼承中國人  實現騎車、開車介面
class XiaoMing : ZHHuman(), RideBike, DriveCar {
    override fun drive() {
        println("小明學會了開車")
    }
    override fun ride() {
        println("小明學會了騎自行車")
    }
}


//騎車介面
interface RideBike {
    //騎車行為
    fun ride()
}

//開車介面
interface DriveCar {
    //開車行為
    fun drive()
}

5.2、介面細節

Kotlin中介面可以包含屬性申明,kotlin 介面中方法也可以有一個預設實現,有預設實現的方法介面被使用,可以直接使用預設實現,可以不用再次重寫此方法,當然也可以被重寫,使用super.方法名來呼叫預設實現。

fun main(args: Array<String>) {
    val xiaoFang = XiaoFang("12345678")
    xiaoFang.drive()
    //列印結果:
    // 出示駕照12345678
    // 掛擋  踩油門 走
    // 小方學會了開車
}

//小方 繼承中國人  實現騎車、開車介面
class XiaoFang(override var license: String) : ZHHuman(), RideBikeNew, DriveCarNew {
    override fun drive() {
        super.drive()
        println("小方學會了開車")
    }
    override fun ride() {
        println("小方小學會了騎自行車")
    }
}

//騎車介面
interface RideBikeNew {
    //騎車行為
    fun ride()
}

/**
 * 開車介面,Kotlin 介面中欄位不可實現(賦值),只能實現類賦值,Java中可以
 * Java 介面中方法不可實現,Kotlin中可以
 */
interface DriveCarNew {
    //駕照號碼
    var license: String
    //開車行為
    fun drive() {
        println("出示駕照${license}")
        println("掛擋  踩油門 走")
    }
}

如果一個類實現了兩個介面中相同的方法,可以使用super<介面名>.方法名 來呼叫介面中預設實現。

interface A {
    fun foo() { print("A") }
    fun bar()
}
interface B {
    fun foo() { print("B") }
    fun bar() { print("bar") }
}

class C : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }
    override fun bar() {
        super<B>.bar()
    }
}

六、多型

多型:同一種功能多種表現形式

fun main(args: Array<String>) {
//    多型特點:通過父類接收  執行的是子類的方法
    val dog:Animal = Dog("黑色")
    println("毛髮顏色:${dog.color},動態行為:${dog.call()}")//毛髮顏色:黑色,動態行為:狗汪汪叫
    val cat:Animal = Cat("白色")
    println("毛髮顏色:${cat.color},動態行為:${cat.call()}")//毛髮顏色:白色,動態行為:貓喵喵叫
}

//動物抽象類
abstract class Animal {
    //靜態屬性 毛髮顏色
    abstract var color: String
    //動態行為
    abstract fun call():String
}

//狗 實體類
class Dog(override var color: String) : Animal() {
    override fun call(): String{
        return