Kotlin 面向对象(一)

【文字内容源于《疯狂Kotlin讲义》,代码内容原创】

目录

一、类和对象

1.定义类

2.对象的产生和使用

3.对象的this引用

二、方法详解

1.方法与函数的关系

2.中缀表示法

3.componentN方法与解构

思考:遍历map

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.中缀表示法

Kotlin 的方法还可使用 infix 修饰,这样该方法就可通过中缀表示法调用,就像这些方法 是双目运算符一样。需要指出的是, infix 方法只能有一 个参数一一原因很简单,因为双目运算符的后面只能带 1个参数。

中缀表达式的要求:

  • 中缀表达式必须是扩展函数方法
  • 中缀表达式只能有一个参数
  • 中缀表达式的参数不能有默认值(否则参数可能为空)
  • 中缀表达式的参数不能是可变参数(否则参数可能不止一个)
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
}

在Kotlin的数据类编译过程中,在声明数据类的时候,会自动生成 componentN() 方法,对应按声明顺序出现的所有属性。

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()方法),那么即可通过将它们放在括号中引入多个新参数来代替单个参数。

PermissionX.init(this@XXXXX)
                .permissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                .request { allGranted, grantedList, deniedList ->
                    {
                        println("$allGranted, $deniedList")
                    }
                }

由于“grantedList”参数并未被使用,可用_代替,如下:
PermissionX.init(this@XXXXX)
                .permissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                .request { allGranted, _, deniedList ->
                    {
                        println("$allGranted, $deniedList")
                    }
                }

三、属性和字段

1、读写属性和只读属性

Kotlin 使用 val 定义只读属性,使用 var 定义读写属性,系统会为只读属性生成getter方法,会为读写属性生成 getter + setter 方法。

在定义 Ko tlin 普通属性时,需要程序员显式指定初始值:要么在定义时指定初始值,要么在构造器中指定初始值。
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 修饰的属性。 Kotlin 不会为幕后属性生成任何 getter、setter 方法。 因此程序不能直接访问幕后属性,必须由开发者为幕后属性提供 getter、setter 方法。
//幕后属性
private var _taste:String = ""

5、延迟初始化属性

Kotlin 提供了 lateinit 修饰符来解决属性的延迟初始化。使用 lateinit 修饰的属性,可以在定义该属性时和在构造器中都不指定初始值。

lateinit 修饰符有以下限制。
  • lateinit 只能修饰在类体中声明的可变属性(使用val声明的属性不行,在主构造器中声明的属性也不行)
  • lateinit 修饰的属性不能有自定义的getter、setter方法
  • lateinit 修饰的属性必须是非空类型
  • lateinit 修饰的属性不能是原生类型(即 Java 种基本类型对应的类型)
Java 不同的是, Kotlin 不会为属性执行默认初始化。因此,如果在 lateinit 属性赋初始值之前访问它,程序将会引发“ lateinit property name has not been initialized 异常。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值