【Kotlin】类和对象

1 前言

Kotlin 是面向对象编程语言,与 Java 语言类似,都有类、对象、属性、构造函数、成员函数,都有封装、继承、多态三大特性,不同点如下。

  • Java 有静态(static)代码块,Kotlin 没有;
  • Java 有静态(static)方法,Kotlin 没有;
  • Java 构造函数名与类名相同,Kotlin 构造函数名为 constructor;
  • Kotlin 有初始化代码块(init),Java 没有;
  • Kotlin 有主构造函数,Java 没有。

在包下面右键,依次点击【New → Kotlin Class/File】,输入类名后,创建 Kotlin 类文件。

如下,创建了一个 Student.kt 文件。

Kotlin 复制代码
package com.zhyan8.kotlinStudy

class Student {
}

笔者为简化代码,将定义的类与 main 函数放在同一个文件中了。

2 类的结构

如下,Student 类是一个自定义的类,里面包含了一个类的基本结构。

Kotlin 复制代码
fun main() {
    var stu1 = Student()
    stu1.study()
    println("-----------------------------------------")
    var stu2 = Student("li si", 23)
}

class Student {
    private var name: String = "zhang san" // 属性
        get() { // name的getter方法
            return field
        }
        set(value) { // name的setter方法
            field = value
        }

    private var age: Int = 18 // 属性

    init { // 初始化代码块, 在构造函数前执行
        println("Student init, name=$name, age=$age")
    }

    constructor() { // 无参构造函数
        println("create-1, name=$name, age=$age")
    }

    constructor(name: String, age: Int) { // 有参构造函数
        println("create-2, name=$name, age=$age")
        this.name = name
        this.age = age
    }

    fun study() { // 成员方法
        println("study...")
    }
}

说明:init 代码块可以有多个,按照从前往后的顺序执行;上述构造函数都是次要构造函数,第 3 节中会介绍主构造函数。

运行程序后,打印如下。

Kotlin 复制代码
Student init, name=zhang san, age=18
create-1, name=zhang san, age=18
study...
-----------------------------------------
Student init, name=zhang san, age=18
create-2, name=li si, age=23

3 主构造函数

主构造函数是紧接在类名后面的构造函数,次要构造函数是类体内部定义的构造函数,它们的区别如下。

  • 主构造函数:主构造函数只能存在一个,只有函数声明,没有函数体,可以在入参中定义类的属性,会自动进行类属性的初始化赋值。
  • 次要构造函数:次要构造函数可以存在多个,可以自定义函数体,也可以无函数体,不能在入参中定义类属性,当类有主构造函数时,所有次要构造函数必须直接或间接地调用主构造函数。

3.1 无参主构造函数

Kotlin 复制代码
fun main() {
    var stu1 = Student()
    println("-----------------------------------------")
    var stu2 = Student("zhang san")
}

class Student() { // 等价与: class Student constructor()
    init { // 初始化代码块, 在构造函数前执行
        println("init")
    }

    constructor(name: String): this() {
        println("constructor, name=$name")
    }
}

运行程序后,打印如下。

Kotlin 复制代码
init
-----------------------------------------
init
constructor, name=zhang san

class Student() 等价于 class Student constructor(),如果需要对主构造函数的权限进行控制,可以修改如下。

Kotlin 复制代码
class Student private constructor() {
    ...
}

3.2 有参主构造函数(普通参数)

Kotlin 复制代码
fun main() {
    var stu1 = Student("xiao ming", 23)
    println("-----------------------------------------")
    // stu1.name // 编译报错, name不是成员属性
    var stu2 = Student()
}

class Student(name: String, age: Int) {
    init {
        println("init, name=$name, age=$age")
    }

    constructor(): this("zhang san", 18) {
        println("constructor")
    }
}

运行程序后,打印如下。

Kotlin 复制代码
init, name=xiao ming, age=23
-----------------------------------------
init, name=zhang san, age=18
constructor

3.3 有参主构造函数(成员属性)

Kotlin 复制代码
fun main() {
    var stu1 = Student("xiao ming", 23)
    println("stu1.name=${stu1.name}, stu1.age=${stu1.age}")
    println("-----------------------------------------")
    var stu2 = Student()
    println("stu2.name=${stu2.name}, stu2.age=${stu2.age}")
}

class Student(var name: String, var age: Int) {
    init {
        println("init, name=$name, age=$age")
    }

    constructor(): this("zhang san", 18) {
        println("constructor")
    }
}

说明:在主构造函数中,通过给入参添加 var(变量)或 val(常量)修饰,使得参数变为成员属性;在次要构造函数中,不能给入参添加 var 或 val 修饰。

运行程序后,打印如下。

Kotlin 复制代码
init, name=xiao ming, age=23
stu1.name=xiao ming, stu1.age=23
-----------------------------------------
init, name=zhang san, age=18
constructor
stu2.name=zhang san, stu2.age=18

如果用户想修改入参属性的权限,可以在 var 或 val 前面添加权限修饰符。

Kotlin 复制代码
class Student(private val name: String, protected var age: Int) {
    ...
}

4 封装

封装是指将相关联的属性和函数封装到同一个类中,并且可以控制这些属性和方法的访问权限,它通过隐藏内部细节和提供清晰的接口,提高了代码的安全性、可维护性和可理解性,它是面向对象编程中的重要概念之一。

在 Kotlin 中,有四种访问权限修饰符:private、protected、internal 和 public。这些修饰符控制了代码中类、函数、属性等成员的可见性和访问权限。

  • private:最严格的访问权限,只在声明它的类或文件内可见。
  • protected:与 Java 中的 protected 类似,不同之处在于 Kotlin 中 protected 修饰的成员仅对其子类可见,但不一定在同一个文件中可见。另外,protected 在 Kotlin 中不能直接应用于顶层函数和属性(直接定义在文件中的函数和属性,而不是在类中定义的)。
  • internal:模块内可见(模块是编译在一起的一组 Kotlin 文件),internal 修饰的成员对于同一模块中的任何其他代码都是可见的,但对于其他模块中的代码是不可见的。
  • public:最宽松的访问权限,public 成员可以被任何地方的代码访问,如果没有指定访问修饰符,默认为 public。

5 继承

继承是指一个类(称为子类或派生类)基于另一个类(称为父类或基类)创建新类,子类继承了父类的属性和方法,并且可以在此基础上进行扩展或修改,它是面向对象编程中的重要概念之一。在 Kotlin 中,继承使用冒号(:)来表示,Any 类是所有类的基类。

5.1 子类无主构造函数

Kotlin 复制代码
fun main() {
    var stu = Student("zhang san", 23, 1001)
    println("-----------------------------------------")
    var tea = Teacher("lao li", 23, 101)
}

open class People(var name: String) {
    init {
        println("People init, name=$name")
    }

    constructor(name: String, age: Int): this(name) {
        println("People constructor, name=$name, age=$age")
    }
}

class Student : People {
    init {
        println("Student init, name=$name")
    }

    constructor(name: String, age: Int, id: Int) : super(name) {
        println("Student constructor, name=$name, age=$age, id=$id")
    }
}

class Teacher : People {
    init {
        println("Teacher init, name=$name")
    }

    constructor(name: String, age: Int, id: Int) : super(name, age) {
        println("Teacher constructor, name=$name, age=$age, id=$id")
    }
}

说明:子类必须直接或间接调用一下父类的一个构造函数,否则编译报错。

运行程序后,打印如下。

Kotlin 复制代码
People init, name=zhang san
Student init, name=zhang san
Student constructor, name=zhang san, age=23, id=1001
-----------------------------------------
People init, name=lao li
People constructor, name=lao li, age=23
Teacher init, name=lao li
Teacher constructor, name=lao li, age=23, id=101

5.2 子类有主构造函数

Kotlin 复制代码
fun main() {
    var stu = Student("zhang san", 23, 1001)
    println("-----------------------------------------")
    var tea = Teacher("lao li", 23)
}

open class People(var name: String) {
    init {
        println("People init, name=$name")
    }

    constructor(name: String, age: Int): this(name) {
        println("People constructor, name=$name, age=$age")
    }
}

class Student(name: String, var age: Int, var id: Int) : People(name) {
    init {
        println("Student init, name=$name, age=$age, id=$id")
    }
}

class Teacher(name: String, var age: Int, var id: Int) : People(name, age) {
    init {
        println("Teacher init, name=$name, age=$age, id=$id")
    }

    // 这里this不能替换为super, 不能绕过子类的主构造函数
    constructor(name: String, age: Int) : this(name, age, 0) {
        println("Teacher constructor, name=$name, age=$age, id=$id")
    }
}

说明:子类必须直接或间接调用一下父类的一个构造函数,否则编译报错;当子类中有主构造函数时,子类中的次要构造函数。

运行程序后,打印如下。

Kotlin 复制代码
People init, name=zhang san
Student init, name=zhang san, age=23, id=1001
-----------------------------------------
People init, name=lao li
People constructor, name=lao li, age=23
Teacher init, name=lao li, age=23, id=0
Teacher constructor, name=lao li, age=23, id=0

6 多态

多态是指同一个函数可以在不同的对象上表现出不同的行为,这种行为通常通过继承和接口来实现。多态使得代码更加灵活和可扩展,是面向对象编程中的重要概念之一。

6.1 覆盖方法

Kotlin 复制代码
fun main() {
    var peo: People = Student("li si", 25, 1002)
    peo.say()
}

open class People(var name: String, var age: Int) {
    init {
        println("People init, name=$name, age=$age")
    }

    open fun say() {
        println("People say")
    }
}

class Student(name: String, age: Int, var id: Int) : People(name, age) {
    init {
        println("Student init, name=$name, age=$age, id=$id")
    }

    override fun say() {
        println("Student say")
    }
}

运行程序后,打印如下。

Kotlin 复制代码
People init, name=li si, age=25
Student init, name=li si, age=25, id=1002
Student say

6.2 覆盖属性

Kotlin 复制代码
fun main() {
    var peo : People = Student()
    peo.doSomething()
}

open class People {
    open var name: String = "zhang san"

    fun doSomething() {
        println("doSomething, name=$name")
    }
}

class Student : People() {
    override var name: String = "li si"
}

运行程序后,打印如下。

Kotlin 复制代码
doSomething, name=li si

6.3 类型智能转换

Kotlin 复制代码
fun main() {
    var peo: People = Student()
    // peo.study() // 编译报错
    if (peo is Student) {
        peo.study() // 智能转换为Student
    }
}

open class People {
}

class Student : People() {
    fun study() {
        println("study...")
    }
}

说明:Java 没有智能转换特性,需要进行强制类型转换。

相关推荐
前行的小黑炭17 小时前
Android :如何提升代码的扩展性,方便复制到其他项目不会粘合太多逻辑,增强你的实战经验。
android·java·kotlin
珠峰下的沙砾18 小时前
在kotlin中如何使用像java中的static
kotlin
用户092 天前
Android View 事件分发机制详解及应用
android·kotlin
ForteScarlet2 天前
Kotlin 2.2.20 现已发布!下个版本的特性抢先看!
android·开发语言·kotlin·jetbrains
珠峰下的沙砾2 天前
Kotlin中抽象类和开放类
kotlin
Kapaseker2 天前
如果你的 View 不支持 Compose 怎么办
android·kotlin
前行的小黑炭2 天前
Android:在项目当中可能会遇到的ANR,应该如何解决?
android·java·kotlin
FunnySaltyFish2 天前
Kotlin 2.2.20 上新:新contract、跨平台编译稳定、默认Swift导出……
kotlin
alexhilton2 天前
runBlocking实践:哪里该使用,哪里不该用
android·kotlin·android jetpack
萧雾宇2 天前
Android Compose打造仿现实逼真的烟花特效
android·flutter·kotlin