#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 写法,直接将属性定义在构造器中
// 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
表示向前引用属性
类的继承
和其它语言不通之处
- 子类继承时,父类需要完成构造
这个感觉有点怪怪的,和其它语言有点差别,以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构造器中的参数,实际上属性,继承时要确保父类中的属性初始化完成(个人觉得写法有点别扭😝)
- 访问修饰符
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()
- 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和其它语言一样,只能继承一个类,但是可以实现多个接口。
- 一般写法
使用 interface
关键词,可以给出实现,也可以不给出实现
// 不给出实现, 则实体类必须自己提供实现
interface IFlyable {
fun fly()
}
// 给出实现
// 实体类可以选择 override 也可以直接使用接口提供的实现
interface IFlyable {
fun fly() {
println("I can fly")
}
}
- 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
-