前言
Kotlin的面向对象学习笔记
定义类
类中定义主构造函数
- 通过
constructor作为函数名的函数是构造函数
- 主构造函数直接定义在类名后
constructor:主构造函数名
1 2 3
| class 类名 constructor(形参名: 数据类型) { ... }
|
- 如果主构造函数没有权限修饰符,则可以省略主构造函数的函数名
1 2 3
| class 类名(形参名: 数据类型) { ... }
|
类中定义次构造函数
1 2 3 4 5
| class 类名 constructor(var 形参名: 数据类型) { constructor(形参名: 数据类型) { this(形参名) } }
|
- 如果没有显式定义主构造函数,则次构造函数中无需显式的委托给主构造函数,其内部已经隐式的委托给了主构造函数
1 2 3 4 5
| class 类名 { constructor(形参名: 数据类型) { ... } }
|
次构造函数的重载
1 2 3 4 5 6 7 8
| class 类名 { constructor(形参名: 数据类型) { ... } constructor(形参名1: 数据类型, 形参名2: 数据类型) { ... } }
|
类中定义实例属性
1 2 3
| class 类名 { var 实例属性名: 数据类型 = 值 }
|
实例属性延迟初始化
- 通过
lateinit关键字修饰的属性是延迟初始化的属性,可以在声明时不赋初值
- 延迟初始化的属性是懒加载的,必须在使用之前进行赋值
- 只有引用类型才可以延迟初始化,基本类型不能延迟初始化
1 2 3
| class 类名 { lateinit var 延迟初始化属性名: 数据类型 }
|
实例属性的getter和setter
- 没有显式定义getter和setter时,实例属性也有内置的getter和setter
1 2 3 4 5
| class 类名 { var 实例属性名: 数据类型 = 值 get() = field set(value) { field = value } }
|
1 2 3 4
| class 类名 { val 实例属性名: 数据类型 = 值 get() = field }
|
主构造函数通过形参定义实例属性
- 主构造函数的形参可以被
var或val修饰,当主构造函数的形参被var或val修饰时,则表示同时声明了主构造函数的形参名和实例属性名
1 2 3
| class 类名 constructor(var 实例属性名: 数据类型) { ... }
|
类中定义实例方法
1 2 3 4 5
| class 类名 { fun 实例方法名() { ... } }
|
类中定义初始化代码块
- 通过
init定义初始化代码块
- 初始化代码会在构造函数执行前执行
1 2 3 4 5
| class 类名 { init { ... } }
|
1 2 3 4 5 6 7 8
| class 类名 { init { ... } init { ... } }
|
创建对象
- 通过构造函数创建对象,主构造函数和次构造函数都可以用于创建对象
对象访问实例属性
对象访问实例方法
方法通过this关键字访问当前实例
访问当前实例属性
1 2 3 4 5 6 7
| class 类名 { var 实例属性名: 数据类型 = 值 fun 实例方法名() { println(this.实例属性名) this.实例属性名 = 值 } }
|
访问当前实例方法
1 2 3 4 5 6 7 8 9 10 11 12
| class 类名 { fun 实例方法名1() { ... } fun 实例方法名2(): 返回值类型 { ... } fun 实例方法名3() { this.实例方法名1() var 变量名 = this.实例方法名2() } }
|
方法的重载
1 2 3 4 5 6 7 8
| class 类名 { fun 实例方法名() { ... } fun 实例方法名(形参名: 数据类型) { ... } }
|
对象空值
定义可空类型变量
通过判空使用可空类型变量
1 2 3
| if (对象名 != null) { println(对象名.属性名) }
|
强制使用可空类型变量
自动在不为空值时才使用可空类型变量
- 如果对象为空值,且调用了对象的属性或方法,不会报错而是返回空值
当可空类型变量为空值时赋予默认值
解构对象中的属性
- 类中通过定义
component()方法,返回可以被解构的值
- 通过在
component加序号的形式定义结构的顺序
- 通过
_来跳过对应属性的结构
1 2 3 4 5 6 7 8 9 10 11
| class 类名(属性名1: 数据类型, 属性名2: 数据类型) { operator fun component1() = 属性名1 operator fun component2(): 数据类型 { return 属性名2 } }
fun main() { var (变量名1, 变量名2) = 类名(属性名1, 属性名2) var (_, 变量名3) = 类名(属性名1, 属性名2) }
|
权限修饰符
public、无修饰符:当前项目
internal:当钱包
protected:当前类及子类
private:当前类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class 类名 { fun method1() { ... } public fun method2() { ... } internal fun method3() { ... } protected fun method4() { ... } private fun method5() { ... } }
|
继承
- 通过
open关键字允许当前类被继承
- 通过在类名后添加
:父类构造函数继承父类,在调用子类构造函数时,父类的构造函数也会被执行
1 2 3 4 5
| open class GrandFather {}
open class Father: GrandFather() {}
class Son : Father() {}
|
- 通过
this()调用自己的主构造函数,通过super()调用父类构造函数,次构造函数不能饶过主构造函数调用父类构造函数,必须直接或间接的调用主构造函数
1 2 3 4 5
| open class Father {}
class Son() : Father() { constructor(形参名: 数据类型) : this() }
|
公共父类
Any类中包含equals()、hashCode()、toString()方法
equals()方法与==运算符完全等价,子类重写equals()方法时,必须重写父类equals()方法
调用父类属性
1 2 3 4 5 6 7 8 9
| open class Father { open var 属性名: 数据类型 = 值 }
class Son : Father() { 方法名() { println(super.属性名) } }
|
调用父类方法
1 2 3 4 5 6 7 8 9 10 11
| open class Father { open 方法名() { ... } }
class Son : Father() { 方法名2() { super.方法名() } }
|
重写父类属性
1 2 3 4 5 6 7
| open class Father { open var 属性名: 数据类型 = 值 }
class Son(override var 属性名: 数据类型) : Father() { ... }
|
1 2 3 4 5 6 7
| open class Father { open var 属性名: 数据类型 = 值 }
class Son : Father() { override var 属性名: 数据类型 = 值 }
|
重写父类方法
1 2 3 4 5 6 7 8 9 10 11
| open class Father { 方法名() { ... } }
class Son : Father() { override 方法名() { ... } }
|
多态
1 2 3 4 5 6 7
| open class Father {}
class Son : Father() {}
fun main() { var 对象名: Father = Son() }
|
判断对象属于指定类
判断对象不属于指定类
强制类型转换
强制转换可空类型
抽象类
- 通过
abstract关键字定义的类是抽象类,抽象类中可以通过abstract关键字定义抽象属性和抽象方法
1 2 3 4
| abstract class 抽象类名 { abstract var 抽象属性名: 数据类型 abstract fun 抽象方法名() }
|
1 2 3 4 5 6
| class 类名 : 抽象类名() { override var 抽象属性名: 数据类型 = 值 override fun 抽象方法名() { ... } }
|
接口
- 通过
interface关键字定义的类是接口,接口中可以定义抽象属性和抽象方法,接口中的抽象属性和抽象方法无需使用abstract关键字定义
- 接口中可以为抽象属性定义Getter方法
- 接口中可以为抽象方法定义默认实现
1 2 3 4 5 6 7
| interface class 抽象类名 { var 抽象属性名: 数据类型 get() = 值 fun 抽象方法名() { ... } }
|
1 2 3 4 5 6
| class 类名 : 接口1, 接口2 { override var 抽象属性名: 数据类型 = 值 override fun 抽象方法名() { ... } }
|
接口冲突
1 2 3 4 5 6 7 8 9 10 11
| interface class 接口名1 { fun method() {} } interface class 接口名2 { fun method() {} } class 类名 : 接口名1, 接口名2 { override fun method() { super<接口名1>.method() } }
|
方法和属性的扩展
为指定类扩展实例方法
- 扩展方法的this指向调用的对象,this可以省略
1 2 3 4 5 6 7 8 9 10
| class 类名 {}
fun 类名.扩展方法名() { ... }
fun main() { var 对象名 = 类名() 对象名.扩展方法名() }
|
通过Lambda表达式定义扩展方法
1 2 3 4 5 6 7 8 9 10 11
| class 类名 {}
fun main() {
var fn: 类名.扩展方法名(形参数据类型) -> 返回值类型 = { ... } var 对象名 = 类名() fn(对象名, 实参) }
|
为指定类扩展实例实例
1 2 3 4 5 6 7 8 9
| class 类名 {}
fun 类名.扩展属性名: 数据类型 get() = 值
fun main() { var 对象名 = 类名() println(对象名.扩展属性名) }
|
扩展方法冲突
1 2 3 4 5 6 7 8 9 10 11 12 13
| class 类名 { fun 扩展方法名() { ... } fun 方法名 { this.扩展方法名() this@类名.扩展方法名() } }
fun 类名.扩展方法名() { ... }
|
匿名类
通过匿名类创建对象
1 2 3 4 5
| var 对象名 = object { var 属性名: 数据类型 = 值 fun 方法名() {} }
|
通过匿名子类创建对象
1 2 3 4 5
| var 对象名 = object : 父类 { var 属性名: 数据类型 = 值 fun 方法名() {} }
|
函数式接口
函数式接口的定义
1 2 3
| fun interface 函数式接口名 { fun 单一抽象方法名() }
|
函数式接口的使用
1 2 3 4 5
| var 方法名 = 函数式接口名 { 单一抽象方法实现 }
方法名()
|
单例类
定义单例类
1 2 3 4 5
| object 类名 { var 属性名: 数据类型 = 值 fun 方法名() {} }
|
通过单例类创建单实例
伴生对象
- 伴生对象中定义的属性和方法可以通过类名直接访问
- 伴生对象是单例的
定义伴生对象
- 通过
companion object关键字在类中定义伴生对象
1 2 3 4 5 6 7
| class 类名 { companion object { var 属性名: 数据类型 = 值 fun 方法名() {} } }
|
通过类名访问属性和方法
委托模式
委托抽象方法的实现
1 2 3 4 5 6 7
| interface 接口名 { fun 抽象方法名() }
class 子类名(var 属性名: 接口名) : 接口名 by 属性名 { 属性名.抽象方法名() }
|
1
| var 变量名: 被委托类名 by 委托类名()
|
通过委托模式实现观察者
1 2 3 4 5
| class 类名 { var 属性名: 数据类型 by Delegates.observe(初始值) { prop, old, new println("$prop 的值发生了变化,从$old 变为 $new") } }
|
委托属性
1 2 3
| class 类名(var 属性名1: 数据类型) { var 属性名2: 数据类型 by ::属性名1 }
|
通过Map为多属性赋值
1 2 3 4 5 6 7 8 9
| class 类名(var map: Map<String, Any>) { var 属性名1: 数据类型 by map var 属性名2: 数据类型 by map }
var 对象名 = 类名(mapOf( 属性名1 to 值, 属性名2 to 值, ))
|
密封类
定义密封类
Unit类型
- 相当于Java中的void
- Kotlin中的Unit是一个单例对象
完成
参考文献
哔哩哔哩——青空の霞光