#8 kotlin class 和 interfaces

kotlin為聲明類 比 swift 中更為簡潔

// swift
class Dog {
  // 屬性
  var name: String
  var weight: Int
  
  init(name: String, weight: Int) {
    self.name = name
    self.weight = weight
  }
}

Kotlin 寫法,直接將屬性定義在構(gòu)造器中

// kotlin
class Dog(val name: String, var weight: Int) {}

// 等價于
class Dog(name: String, weight: Int) {
  val name = name
  var weight = weight
}

另外 需要在構(gòu)造器中使用 val | var 對屬性進(jìn)行定義, val 定義的屬性,賦值后就不能再改變, 即 屬性只讀

var dog = Dog("Lisa", 20)
dog.weight = 30 // weight 屬性使用 var聲明 賦值后可以更改
dog.name = "Doge" // Error 使用 val聲明 不能進(jìn)行更改

初始化塊(initializer block)

和其它語言不通,在對象 構(gòu)造(constructor) 完之后,如果你想做一些復(fù)雜的操作,比如加載數(shù)據(jù)等,可以使用 init 執(zhí)行一些額外的操作

class Dog(val name: String, var weight: Int) {
  // 可以定義多個 init blocks
  // 執(zhí)行順序按照其定義的順序
  init {
    println("the first init block")
  }
  
  init {
    println("the second init block")
  }
}

Getter and Setter

類中每一個使用 var 聲明的屬性都有默認(rèn)的賦一個getter和setter, 而使用 val 聲明的屬性,則只會給一個getter

var someProperty: String
    get() = field
    set(value) {
    field = value
    }

val readOnlyProperty: String
    get() = field

示例:

// swift
class Point {
  var x = 0.0, y = 0.0
}
class Size {
  var width = 0.0, height = 0.0
}
class Rect {
  var origin = Point()
  var size = Size()
  
  var center: Point {
    get {
      let centerX = origin.x + (size.width / 2)
      let centerY = origin.y + (size.height / 2)
      return Point(centerX, centerY)
    }
    set(newCenter) {
        // 根據(jù)新的中心點(diǎn) 推斷出 原點(diǎn)的位置
      origin.x = newCenter - (size.width / 2)
      origin.y = newCenter - (size.height / 2)
    }
  }
}

kotlin 版本:

// kotlin
class Point(var x: Double = 0.0, var y: Double = 0.0) {}
class Size(var width: Double = 0.0, var height: Double = 0.0) {}

class Rect(var origin: Point = Point(), var size: Size = Size()) {
  var center: Point
    get() {
      val centerX = origin.x + (size.width / 2)
      val centerY = origin.y + (size.height / 2)
      return Point(centerX, centerY)
    }
    set(value) {
        // 根據(jù)新的中心點(diǎn) 推斷出 原點(diǎn)的位置
      origin.x = value - (size.width / 2)
      origin.y = value - (size.height / 2)
    }
}

另外一例:

// kotlin
class Dog(val name: String, var weight_param: Int) {
  var weight = weight_param
    set(value) {
      // 注意這里的 field 標(biāo)識符
      if (value > 0) field = value
    }
    
  var weightInKgs: Double
    // 此處get的寫法 如果body只有一句 可以直接=
    get() = weight / 2.2
}

其中:

  • setter 中 field 表示向前引用屬性

類的繼承

和其它語言不通之處

  1. 子類繼承時,父類需要完成構(gòu)造

這個感覺有點(diǎn)怪怪的,和其它語言有點(diǎn)差別,以swift舉例:

// Swift
// 父類
class Car {
  let make: Int
  let model: String
  
  init(make: Int, model: String) {
    self.make = make
    self.model = model
  }
  
  func move() {
    print("A car is moving")
  }
}

// 子類 敞篷車
class ConvertibleCar: Car {
  override func move() {
    print("A convertible car is moving")
  }
}

let myCar = COnvertibleCar(make: 1990, model: "WorksWagen")
myCar.move()

在kotlin中,

// kotlin
open class Car(open val make: Int, open val model: String) {
  open fun move() {
    println("a car is moving")
  }
}

// 父類調(diào)用構(gòu)造器
class ConvertibleCar(override val make: Int, override val model: String): Car(make, model) {
  override fun move() {
    println("a convertible car is moving")
  }
} 

val myCar = ConvertibleCar(1990, "WorksWagen")
myCar.move()

因為kotlin構(gòu)造器中的參數(shù),實際上屬性,繼承時要確保父類中的屬性初始化完成(個人覺得寫法有點(diǎn)別扭??)

  1. 訪問修飾符

kotlin中類和屬性,默認(rèn)修飾符為 final, 如果想要被繼承,屬性想被override,則需要將修飾限定符修改為 open

// kotlin

// Car需要被繼承 則需要使用 open 進(jìn)行修飾 默認(rèn)是 final
// make, model 屬性希望被 override, 則需要 open進(jìn)行修飾
open class Car(open val make: Int, open val model: String) {

    // 子類不能 override 這個方法
    fun bark() {
    println("bark ...")
    }
    
  open fun move() {
    println("a car is moving")
  }
}

// 
class ConvertibleCar(override val make: Int, override val model: String): Car(make, model) {
  override fun move() {
    println("a convertible car is moving")
  }
} 

val myCar = ConvertibleCar(1990, "WorksWagen")
myCar.move()
  1. var 和 val 修飾的屬性

在繼承關(guān)系中,如果在父類中使用 val 聲明的屬性,如果需要修改,則需要使用 override 關(guān)鍵詞,如果使用 var 修飾的屬性,則可以在 init block 中進(jìn)行修改

// val 修飾
open class Car(open val make: Int, open val model: String) {

    open val image = "" // 車的圖片
    
    fun bark() {
    println("bark ...")
    }
    
  open fun move() {
    println("a car is moving")
  }
}
class ConvertibleCar(override val make: Int, override val model: String): Car(make, model) {

    // 需要使用override對父類中的屬性進(jìn)行修改
    override val image: String = "convertibleCar.jpg"
  override fun move() {
    println("a convertible car is moving")
  }
} 

// 使用var 修飾
open class Car(open val make: Int, open val model: String) {

    // open 也可以省略
    var image = "" // 車的圖片
    
    fun bark() {
    println("bark ...")
    }
    
  open fun move() {
    println("a car is moving")
  }
}
class ConvertibleCar(override val make: Int, override val model: String): Car(make, model) {

    // 在 initializer block 中對父類屬性進(jìn)行修改
    init {
      image = "convertibleCar.jpg"
    }
  override fun move() {
    println("a convertible car is moving")
  }
} 

另外 父類中使用 val 聲明的屬性,子類中可以使用 var 進(jìn)行override; 但是父類中使用 var 聲明的屬性,子類中不能使用 val 進(jìn)行override

open class Animal {
  open val name: String = ""
}
class Dog: Animal() {
  // 使用 var 對父類中的name 進(jìn)行修改
  // 本質(zhì)上是在父類屬性的基礎(chǔ)上添加了一個setter 對屬性進(jìn)行了擴(kuò)充
  override var name = "lily"
}

抽象類

當(dāng)一個類使用 abstract 聲明為抽象類時,不需要使用 open 進(jìn)行修飾.

abstract class Animal {}

kotlin中的抽象類和其他語言基本類似,抽象類可以擁有抽象屬性和抽象方法,子類必須對抽象類中定義的抽象屬性和方法給出實現(xiàn)

abstract class Animal {
  abstract val image: String
  abstract val food: String  
  var hunger = 10
  
  abstract fun makeNoise()
  abstract fun eat()
  
  open fun roam() {
    println("animal is roaming")
  }
  
  fun sleep() {
    println("animal is sleeping")
  }
}

// 犬科
abstract class Canine: Animal() {
  override fun roam() {
    println("the canine is roaming)
  }
}

// 實體類
// 需要對抽象類中的屬性和方法進(jìn)行實現(xiàn)
class Wolf: Canine() {
  override val image = "wolf.jpg"
  override val food = "meat"
  
  override fun makeNoise() {
    println("Hooowwwl")
  }
  
  override fun eat() {
    println("the wolf is eating $food")
  }
}

接口

kotlin和其它語言一樣,只能繼承一個類,但是可以實現(xiàn)多個接口。

  1. 一般寫法

使用 interface 關(guān)鍵詞,可以給出實現(xiàn),也可以不給出實現(xiàn)

// 不給出實現(xiàn), 則實體類必須自己提供實現(xiàn)
interface IFlyable {
  fun fly()
}

// 給出實現(xiàn)
// 實體類可以選擇 override 也可以直接使用接口提供的實現(xiàn)
interface IFlyable {
  fun fly() {
    println("I can fly")
  }
}
  1. Getter & setter

接口中還可以定義屬性,屬性能通過 get() 返回一個值,注意,不能直接進(jìn)行賦值初始化,因為接口沒有構(gòu)造器

// 錯誤寫法
interface IFlyable {
  fun fly()
  
  // error: Property initializers are not allowed in interfaces
  val velocity: Int = 20
}

// 正確寫法
interface IFlyable {
  fun fly()
  
  // 注意這里要用val 不能使用var
  val velocity: Int
    get() = 20
}

setter 中不能使用 field, 因為接口中不存在向后引用字段

// 錯誤寫法
interface IFlyable {
  fun fly()
  
    // error: Property in an interface cannot have a backing field
  var velocity: Int
    get() = 20
    set(value) {
      if value > 0 {
        field = value
      }
    }
}

// 正確寫法
interface IFlyable {
  fun fly()
  
    // 這里要使用var 因為val是只讀的 不存在setter
  var velocity: Int
    get() = 20
    set(value) {
      if value > 0 {
        // 不使用 field 的其它操作 都可以
        print("your value is greater than 0")
      }
    }
}

is & as & when

  • is 用來判斷是否是某個類型,!is 這個則相反 (第一次看到這種寫法)

    abstract class Animal {
      abstract val food: String
      
      fun eat() {
        println("animal is eating $food")
      }
    }
    
    class Dog: Animal() {
      override val food = "meat"
      
      fun bark() {
        println("a dog is barking")
      }
    }
    
    var a = Dog()
    
    if (a is Dog) {
      a.bark() 
    }
    
  • as: 用于進(jìn)行類型轉(zhuǎn)換, is 會執(zhí)行智能轉(zhuǎn)換,但是有時候智能轉(zhuǎn)換會失敗,需要使用 as 進(jìn)行顯式的轉(zhuǎn)換

    interface IRunable {
      fun run()
    }
    
    abstract class Animal: IRunable {
      abstract val food: String
      
      fun eat() {
        println("animal is eating $food")
      }
      
      override fun run() {
        println("some animal can run")
      }
    }
    
    class Dog: Animal() {
      override val food = "meat"
      
      fun bark() {
        println("a dog is barking")
      }
    }
    
    class Ground {
      // 使用var 聲明 表示something可能會發(fā)生變化
      var something: IRunable = Dog()
      
      fun showInfo() {
          // 看著沒什么問題 但是編輯器會報錯
          // Smart cast to 'Dog' is impossible because 'something' is a mutable property that
          // could hae been changed by this time
        if (something is Dog) {
          something.bark()
        }
      }
    }
    
    // 解決辦法, 使用as 顯式的轉(zhuǎn)換
    class Ground {
      // 使用var 聲明 表示something可能會發(fā)生變化
      var something: IRunable = Dog()
      
      fun showInfo() {
        if (something is Dog) {
          (something as Dog).bark() // 進(jìn)行顯式轉(zhuǎn)換
        }
      }
    }
    
  • when: 這個就是其它語言中的 switch...case

    // 用法1 聲明變量
    var temperature = 30
    var isCool: Boolean = when {
      temperature > 28 -> false
      else -> true
    }
    
    // 用法2 和 is 搭配
    when (animal) {
      is Dog -> {
        // ...
      }
      is Cat -> {
        // ...
      }
    }
    

大致內(nèi)容:

  • 類的定義

  • 初始化塊

  • getter 和 setter (field 關(guān)鍵詞)

  • 類的繼承

    • 注意修飾符的使用 open | final
    • 抽象類
    • var & val 修飾屬性的差異,override 時需要注意, 還有 init block 的使用
  • 接口的使用

    • 可以提供默認(rèn)實現(xiàn) 也可以是抽象的
    • 可以使用getter 和 setter, 但是setter 中不能使用 field
  • 輔助邏輯關(guān)鍵詞

    • is & !is

    • as

    • when

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容