Kotlin总结之四面向对象编程
一.Kotlin类的创建
1.类的声明
Kotlin 类可以包含:构造函数和初始化代码块、函数、属性、内部类、对象声明。
Kotlin 中使用关键字 class 声明类,后面紧跟类名:
1 | // 类名为 Apple |
当类没有结构体的时候,大括号可以省略。如:
1 | class Apple |
| 类的修饰符 | 描述 |
|---|---|
| abstract | 抽象类 |
| final | 类不可继承,默认属性 |
| enum | 枚举类 |
| open | 类可继承,类默认是final的 |
| annotation | 注解类 |
| 访问权限修饰符 | 描述 |
|---|---|
| private | 仅在同一个文件中可见 |
| protected | 同一个文件中或子类可见 |
| public | 所有调用的地方都可见 |
| internal | 同一个模块中可见 |
2. 类的构造函数
在kotlin中有两种类型的构造函数:
- 主构造函数(主构造器)。
- 次级构造函数(次级构造器)。
在Kotlin类中只有一个主构造函数(主构造器),而辅助构造函数(次级构造器)可以是一个或者多个。
(1.)主构造函数
主构造函数是类头的一部分:它跟在类名(和可选的类型参数)后。
1 | class 类名 construction(参数1,参数2….){ } |
如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字,否则不能省略。
不能省略:
1 | class Person private constructor(name:String){ |
可以省略:
1 | class Person(name: String) { |
如果不需要将构造函数中参数同时作为类属性,也可以写成如下形式(constructor表示构造函数,里面执行初始化的处理):
1 | class Person{ |
由于主构造函数没有函数体。如果需要在主构造函数中编写代码该怎么做?初始化的代码可以放 到以 init 关键字作为前缀的初始化块(initializer blocks)中,可以在这里编写要在主构造函数中完成的业务,init{…}中能使用构造函数中的参数:
1 | class Person(username: String, age: Int){ |
注意,主构造的参数可以在初始化块中使用。它们也可以在 类体内声明的属性初始化器中使用:
1 | class Person(name: String) { |
事实上,声明属性以及从主构造函数初始化属性,Kotlin 有简洁的语法:
1 | class Person(var name: String, var age: Int) { |
与普通属性一样,主构造函数中声明的属性可以是 可变的(var)或只读的(val)。
(2.)次构造函数
类也可以声明前缀有 constructor的次构造函数,可以声明多个次构造函数:
1 | class Person { |
- kotlin声明了主构造器
如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数 用 this 关键字即可:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Person (name : String?,age : Int) {
var name : String?
var age : Int
init {
println("执行初始化块")
this.name = name
this.age = age
}
constructor() : this(null,0) {//直接委托调用主构造器
}
constructor(name : String) : this() {//间接委托调用主构造器
}
} - kotlin没声明主构造器
kotlin没声明主构造器,重载构造器不需要调用当前类的其他构造器,调用构造器时也会执行初始化块。class Person private constructor () {1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24class Person {
var name : String?
var age : Int
init {
println("执行初始化块")
}
//在各自的构造器中对属性进行赋值
constructor() {
this.name = null
this.age = 0
}
constructor(name : String) {
this.name = name
this.age = 0
}
constructor(name : String, age : Int) {
this.name = name
this.age = age
}
}
````
如果一个非抽象类没有声明任何(主或次)构造函数,它会有一个生成的 不带参数的主构造函数。构造函数的可见性是 public。如果你不希望你的类 有一个公有构造函数,你需要声明一个带有非默认可见性的空的主构造函数(此时这个类不能够实例化):
}
1 |
|
class Person // 从 Any 隐式继承
1 | > Any不是 java.lang.Object;尤其是,它除了 equals()、hashCode()和toString()外没有其他属性或者方法。 |
open class Parent(p: Int)//申明该类是open修饰的,该类可以继承
class Child(p: Int) : Parent(p)//继承open类
1 | 方法重写: |
//父类
open class Parent{
open fun method(){}
fun unOpen(){ //没有open显示指定,这个函数不能被重写覆盖
println(“unOpen”)
}
}
//子类
class Child: Parent() {
///重写覆盖的方法这里必须使用override关键字来修饰
override fun method() {
super.method()
}
override fun unOpen(){ //Child的子类不可再被重写覆盖
super.open()
}
}
1 | >覆盖属性和覆盖方法类似。 |
abstract class AbsBase{
abstract fun method()
}
1 | >在kotlin中,被继承的类需要用关键字open声明,表明该类可以被继承,但是抽象类或者抽象函数是不用 open 标注的。但是如果子类要实现抽象类的非抽象函数,需要在抽象类中将其声明为open。 |
abstract class AbsBase{
abstract fun method() // 如果子类要实现需声明为抽象
open fun method1(){//非抽象方法如果要类子类实现,需要声明为open
println(“输出”)
}
}
class Child : AbsBase() {
override fun method() {
//抽象类的实现
}
override fun method1() {
super.method1()
println(“子类实现”)
}
}
1 |
|
interface Animal {
val kind: String
val color: String
fun doing()
fun eat() {
println("吃骨头")
}
}
class Dog : Animal {
override val kind = "小黑狗"
override val color = "黑色的"
override fun doing() {
println("正在玩")
}
}
1 |
|
interface A {
fun foo() {
println(“A”)
}
fun bar()
}
interface B {
fun foo() {
println(“B”)
}
fun bar() {
println(“bar”)
}
}
class C : A {
override fun bar() {
println(“C.bar”)
}
}
class D : A, B {
override fun foo() {
super.foo()
super.foo()
println(“D.foo”)
}
override fun bar() {
super<B>.bar()
}
}
fun main(args: Array
var d = D()
d.foo()
d.bar()
}
1 |
|
class Outer { // 外部类
private val value: Int = 1
class Nested { // 嵌套类
var inValue = 2
fun nestedMethod() {
// println(value) //嵌套类无法直接访问外部类属性
println(Outer().value) //但可以通过外部类的对象访问,因为有private修饰符,普通类通过对象也无法访问
}
}
fun outerMethod() {
// println(inValue) //外部类无法直接访问嵌套类的属性
// method() //外部类无法直接访问嵌套类的方法
println(Nested().inValue) //外部类无法通过嵌套类的对象访问private属性,但是可以访问public
}
}
fun main() {
val demo = Outer.Nested().inValue // 调用格式:外部类.嵌套类.嵌套类方法/属性
println(demo) // == 2
}
1 |
|
class Outer {
private val value1: Int = 1
var value2 = “成员属性”
//嵌套内部类
inner class Inner {
fun innerFun() = value1 // 访问外部类成员
fun innerTest() {
val outer = this@Outer //获取外部类的成员变量
println("内部类可以引用外部类的成员,bar:${innerFun()}")
println("内部类可以引用外部类的成员,例如:${outer.value2}")
}
}
}
fun main(args: Array
val demo = Outer().Inner().innerFun()
println(demo) // 1
Outer().Inner().innerTest()
}
1 | 为了消除歧义,要访问来自外部作用域的 this,我们使用this@label,其中 @label是一个 代指 this 来源的标签。 |
interface OnClickListener {
fun onClick()
}
class View {
fun setClickListener(clickListener: OnClickListener) {
}
}
fun main(args: Array
val view = View()
view.setClickListener(object : OnClickListener {
override fun onClick() {
}
})
}
1 |
|
class Util {
companion object {
fun getCurrentVersion(): String {
return BuildConfig.VERSION_NAME
}
}
}
1 | 调用: |
var version_name2 = Util .getCurrentVersion()
1 | 伴生对象的特点: |
data class <类名> <(主构造函数参数列表)> [:继承类和实现接口] [(/类体/)]
1 | 主构造函数的参数列表必须使用val或var声明为类属性,而且要求 **至少有一个**,否则无法通过编译。 |
data class User(val id:Int, val name:String)
1 | 数据类的特点: |
interface NetworkStatus
class Loading : NetworkStatus()
class Successed : NetworkStatus()
fun getResultMsg(result: NetworkStatus) = when(result) {
is Loading -> {}
is Successed -> {}
else -> throw IllegalArgumentException()
}
1 | 这里定义了一个 NetworkStatus 接口,然后定义了两个类去实现 NetworkStatus 接口。然后再写一个 getResultMsg() 方法。实际上 Result 的执行结果只能是 Loading 或 Successed,所以里边的 else 永远无法走到,但我们不得不写上,只是为了满足 Kotlin 编译器的语法检查而已。 |
sealed class NetworkStatus
class Loading : NetworkStatus()
class Successed : NetworkStatus()
fun getResultMsg(result: NetworkStatus) = when(result) {
is Loading -> {}
is Successed -> {}
}
1 | 如果添加了一个新的分支,编译器就会发现有的地方发生了改变,编译器就会提醒处理。 |
sealed class NetworkStatus
class Loading : NetworkStatus()
class Successed : NetworkStatus()
class Error(val code: Int, val message: String) : NetworkStatus()
fun getResultMsg(result: NetworkStatus) = when(result) {
is Loading -> {}
is Successed -> {}
is Error->{} //强制要求你将每一个子类所对应的条件全部处理,如果不进行处理编译器就会进行报错提醒
}
参考自:
[kotlin官方文档](https://www.kotlincn.net/docs/reference/constructing-collections.html)