【文字内容源于《疯狂Kotlin讲义》,代码内容原创】
目录
一、类和对象
1.定义类
2.对象的产生和使用
3.对象的this引用
二、方法详解
1.方法与函数的关系
2.中缀表示法
3.componentN方法与解构
4、数据类和返回多个值的函数
5、在Lambda表达式中结构
三、属性和字段
1、读写属性和只读属性
2、自定义getter和setter
3、幕后字段
4、幕后属性
5、延迟初始化属性
一、类和对象
1.定义类
Kotlin 的类定义由类名、类头(指定其泛型声明、主构造器等)和用花括号包围的类体构成。类头和类体都是可选的。对于一个类定义而言,可以包含3种最常见的成员:构造器、属性和方法,这 种成员都可以定义零个或多个,如果三种成员都只定义零个,就是定义了一个空类。空类没有类体,可以省略花括号。
/**
* Created by tzbc on 2022/6/2.
*
* @author tzbc
*/
class Fruit(name: String, color: String) {
var fruitName: String = name
var fruitColor: String = color
fun getFruitInfo(): String {
return "fruitName is $fruitName, fruitColor is $fruitColor";
}
}
2.对象的产生和使用
创建对象的根本途径是构造器,调用某个类的构造器即可创建这个类的对象,并且无须使用new 关键字。
Kotlin 的对象大致有如下作用。
【1】访问对象的属性
【2】调用对象的方法
如果访问权限允许,那么在类中定义的方法和属性都可通过对象来调用。
通过对象访问方法或属性的语法是:对象属性|方法(参数)。
在这种方式中,对象是主调者,用于访问该对象的属性或方法。
val fruit1: Fruit = Fruit("苹果", "红色")
println(fruit1.getFruitInfo())
val fruit2: Fruit = Fruit("香蕉", "黄色")
fruit2.fruitName = "🍌"
println(fruit2.getFruitInfo())
运行结果:
fruitName is 苹果, fruitColor is 红色
fruitName is 🍌, fruitColor is 黄色
3.对象的this引用
Kotlin 也提供了 this 关键字, this 关键字总是指向调用该方法的对象。根据 this 出现位置的不同, this 作为对象的默认引用有两种情形:
【1】在构造器中引用该构造器正在初始化的对象
【2】在方法中引用调用该方法的对象
在后面介绍的扩展函数或带接收者的匿名函数中, this 代表点号左侧传递的接收者。this 关键字最大的作用就是让类中的1个方法访问该类的另1个方法或属性。假设定义了Dog 类,这个 Dog 对象的 run()方法需要调用它的 jump()方法,此时就可通过 this 关键字作为 jump()方法的调用者。
this 可以代表任何对象,当 this 出现在某个方法体中时,它所代表的对象是不确定的,但它的类型是确定的。它所代表的只能是当前类的实例:只有当这个方法被调用时,它所代表的对象才被确定下来(谁在调用这个方法, this 就代表谁)。
class Fruit(name: String, color: String) {
var fruitName: String = name
var fruitColor: String = color
fun getFruitInfo(): String {
println(this.initFruit())
return "fruitName is $fruitName, fruitColor is $fruitColor"
}
private fun initFruit() = "This is fruit!"
}
调用函数:
val fruit = Fruit("苹果", "红色")
println(fruit.getFruitInfo())
运行结果:
This is fruit!
fruitName is 苹果, fruitColor is 红色
Kotlin允许对象的一个成员直接调用另一个成员,可以省略this前缀,也就是说将上面的方法调用改为以下形式也完全正确。
fun getFruitInfo(): String {
println(initFruit())
return "fruitName is $fruitName, fruitColor is $fruitColor"
}
大部分时候,一个方法调用类中的其他方法、属性时无须使用this前缀,但如果方法中有一个局部变量和属性同名,但程序又需要在该方法中访问这个被隐藏的属性,则必须使用this前缀。
二、方法详解
1.方法与函数的关系
Kotlin 的方法与函数其实是统一的,不仅定义函数和方法的语法相同,而且定义在类中的方法依然可独立出来。 也就是说,即使我们将方法定义在类里面,这个方法也依然可以转换为函数。
2.中缀表示法
中缀表达式的要求:
- 中缀表达式必须是扩展函数或方法
- 中缀表达式只能有一个参数
- 中缀表达式的参数不能有默认值(否则参数可能为空)
- 中缀表达式的参数不能是可变参数(否则参数可能不止一个)
infix fun Int.judge(other: Int): String {
return when {
this - other < 0 -> "小于"
this - other > 0 -> "大于"
else -> "等于"
}
}
调用函数:
println(33 judge 41)
运行结果:
小于
3.componentN方法与解构
Kotlin 允许将一个对象N个属性“解构”给多个变量。如果希望将对象解构给多个变量,那么必须为该对象的类定义 componentN()方法 。
程序希望将对象解构给几个变量,就需要为该类定义几个 componentN()方法,且该方法需要使用 operator修饰。
在某些时候,程序希望解构对象后面几个 componen tN ()方 法的返回值、忽略前面几个componentN ()方法的返回值,此时可通过下划线_来占位。
//定义operator修饰的componentN方法,用于解构
operator fun component1(): String {
return this.fruitName
}
//定义operator修饰的componentN方法,用于解构
operator fun component2(): String {
return this.fruitColor
}
调用函数:
val (name, color) = Fruit("苹果", "红色")
//上述代码的实际处理方式为
//val name = Fruit("苹果", "红色").component1()
//val color = Fruit("苹果", "红色").component2()
println(name)
println(color)
val (_, color1) = Fruit("苹果", "红色")
println(color1)
运行结果:
苹果
红色
红色
思考:遍历map
for((key,value) in map){ //使用key、value }
4、数据类和返回多个值的函数
思路:借用解构
fun getMultiFruitByType(int: Int): Fruit {
return when (int) {
0 -> Fruit("苹果", "红色")
1 -> Fruit("香蕉", "黄色")
else -> Fruit("null", "null")
}
}
调用函数:
val (multiName, multiColor) = getMultiFruitByType(1)
println("multiName=$multiName, multiColor=$multiColor")
运行结果:
multiName=香蕉, multiColor=黄色
5、在Lambda表达式中结构
Kotlin 允许对 Lambda 表达式使用解构,如果 Lambda 表达式的参数是支持解构的类型,它们都具有 operator 修饰的 componentN()方法),那么即可通过将它们放在括号中引入多个新参数来代替单个参数。
三、属性和字段
1、读写属性和只读属性
Kotlin 使用 val 定义只读属性,使用 var 定义读写属性,系统会为只读属性生成getter方法,会为读写属性生成 getter + setter 方法。
var apple:String = ""
val banana:String = ""
apple = "苹果"
banana = "香蕉" //error
Kotlin 类中定义属性后,被 Kotlin 程序使用时只能使用点语法访问属性; Java 程序使用时只能通过getter 、setter 方法访问属性。
2、自定义getter和setter
定义getter、setter方法时无需使用fun关键字。
var fruitName: String = name
get() {
println("fruitName get()...")
return "fruitName: $field"
}
set(value) {
println("fruitName set()...")
field = value
}
调用函数:
val orange = Fruit("橙子","橙色")
println(orange.fruitName)
orange.fruitName = "橘色"
运行结果:
fruitName get()...
fruitName: 橙子
fruitName set()...
3、幕后字段
在kotlin中定义1个普通属性时,Kotlin 会为该属性生成field(字段)、getter和setter方法(只读属性没有setter方法〉。 Kotlin为该属性所生成的field就被称为幕后字段(backing field)。
- 该属性使用Kotlin自动生成的getter / setter方法或其中之一。换句话说,对于只读属性,必须重写 getter 方法:对于读写属性,必须重写getter + setter方法,否则总会为该属性生成幕后宇段。
- 重写getter、setter方法时,使用 field 关键字显式引用了幕后字段。
在getter、setter方法中需要通过field关键字引用幕后字段
4、幕后属性
在个别情况下,开发者希望自己定义 field ,并为该 field 提供getter、setter方法,就像 Java所使用的方法。此时可使用 Kotlin 的幕后属性。
//幕后属性
private var _taste:String = ""
5、延迟初始化属性
Kotlin 提供了 lateinit 修饰符来解决属性的延迟初始化。使用 lateinit 修饰的属性,可以在定义该属性时和在构造器中都不指定初始值。
- lateinit 只能修饰在类体中声明的可变属性(使用val声明的属性不行,在主构造器中声明的属性也不行)
- lateinit 修饰的属性不能有自定义的getter、setter方法
- lateinit 修饰的属性必须是非空类型
- lateinit 修饰的属性不能是原生类型(即 Java 种基本类型对应的类型)
Java 不同的是, Kotlin 不会为属性执行默认初始化。因此,如果在 lateinit 属性赋初始值之前访问它,程序将会引发“ lateinit property name has not been initialized 异常。