美文网首页
#8 kotlin class 和 interfaces

#8 kotlin class 和 interfaces

作者: JamesSawyer | 来源:发表于2019-08-16 18:11 被阅读0次

kotlin为声明类 比 swift 中更为简洁

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

Kotlin 写法,直接将属性定义在构造器中

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

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

另外 需要在构造器中使用 val | var 对属性进行定义, val 定义的属性,赋值后就不能再改变, 即 属性只读

var dog = Dog("Lisa", 20)
dog.weight = 30 // weight 属性使用 var声明 赋值后可以更改
dog.name = "Doge" // Error 使用 val声明 不能进行更改

初始化块(initializer block)

和其它语言不通,在对象 构造(constructor) 完之后,如果你想做一些复杂的操作,比如加载数据等,可以使用 init 执行一些额外的操作

class Dog(val name: String, var weight: Int) {
  // 可以定义多个 init blocks
  // 执行顺序按照其定义的顺序
  init {
    println("the first init block")
  }
  
  init {
    println("the second init block")
  }
}

Getter and Setter

类中每一个使用 var 声明的属性都有默认的赋一个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) {
        // 根据新的中心点 推断出 原点的位置
      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) {
        // 根据新的中心点 推断出 原点的位置
      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 标识符
      if (value > 0) field = value
    }
    
  var weightInKgs: Double
    // 此处get的写法 如果body只有一句 可以直接=
    get() = weight / 2.2
}

其中:

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

类的继承

和其它语言不通之处

  1. 子类继承时,父类需要完成构造

这个感觉有点怪怪的,和其它语言有点差别,以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")
  }
}

// 父类调用构造器
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构造器中的参数,实际上属性,继承时要确保父类中的属性初始化完成(个人觉得写法有点别扭😝)

  1. 访问修饰符

kotlin中类和属性,默认修饰符为 final, 如果想要被继承,属性想被override,则需要将修饰限定符修改为 open

// kotlin

// Car需要被继承 则需要使用 open 进行修饰 默认是 final
// make, model 属性希望被 override, 则需要 open进行修饰
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 修饰的属性

在继承关系中,如果在父类中使用 val 声明的属性,如果需要修改,则需要使用 override 关键词,如果使用 var 修饰的属性,则可以在 init block 中进行修改

// 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对父类中的属性进行修改
    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 中对父类属性进行修改
    init {
      image = "convertibleCar.jpg"
    }
  override fun move() {
    println("a convertible car is moving")
  }
} 

另外 父类中使用 val 声明的属性,子类中可以使用 var 进行override; 但是父类中使用 var 声明的属性,子类中不能使用 val 进行override

open class Animal {
  open val name: String = ""
}
class Dog: Animal() {
  // 使用 var 对父类中的name 进行修改
  // 本质上是在父类属性的基础上添加了一个setter 对属性进行了扩充
  override var name = "lily"
}

抽象类

当一个类使用 abstract 声明为抽象类时,不需要使用 open 进行修饰.

abstract class Animal {}

kotlin中的抽象类和其他语言基本类似,抽象类可以拥有抽象属性和抽象方法,子类必须对抽象类中定义的抽象属性和方法给出实现

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)
  }
}

// 实体类
// 需要对抽象类中的属性和方法进行实现
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和其它语言一样,只能继承一个类,但是可以实现多个接口。

  1. 一般写法

使用 interface 关键词,可以给出实现,也可以不给出实现

// 不给出实现, 则实体类必须自己提供实现
interface IFlyable {
  fun fly()
}

// 给出实现
// 实体类可以选择 override 也可以直接使用接口提供的实现
interface IFlyable {
  fun fly() {
    println("I can fly")
  }
}
  1. Getter & setter

接口中还可以定义属性,属性能通过 get() 返回一个值,注意,不能直接进行赋值初始化,因为接口没有构造器

// 错误写法
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: 用于进行类型转换, is 会执行智能转换,但是有时候智能转换会失败,需要使用 as 进行显式的转换

    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可能会发生变化
      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 显式的转换
    class Ground {
      // 使用var 声明 表示something可能会发生变化
      var something: IRunable = Dog()
      
      fun showInfo() {
        if (something is Dog) {
          (something as Dog).bark() // 进行显式转换
        }
      }
    }
    
  • when: 这个就是其它语言中的 switch...case

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

大致内容:

  • 类的定义

  • 初始化块

  • getter 和 setter (field 关键词)

  • 类的继承

    • 注意修饰符的使用 open | final
    • 抽象类
    • var & val 修饰属性的差异,override 时需要注意, 还有 init block 的使用
  • 接口的使用

    • 可以提供默认实现 也可以是抽象的
    • 可以使用getter 和 setter, 但是setter 中不能使用 field
  • 辅助逻辑关键词

    • is & !is

    • as

    • when

相关文章

网友评论

      本文标题:#8 kotlin class 和 interfaces

      本文链接:https://www.haomeiwen.com/subject/bdgqsctx.html