【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 没有智能转换特性,需要进行强制类型转换。

相关推荐
alexhilton2 天前
端侧RAG实战指南
android·kotlin·android jetpack
Kapaseker2 天前
2026年,我们还该不该学编程?
android·kotlin
Kapaseker3 天前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
Kapaseker4 天前
一杯美式搞定 Kotlin 空安全
android·kotlin
FunnySaltyFish5 天前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
Kapaseker5 天前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
Kapaseker6 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
A0微声z8 天前
Kotlin Multiplatform (KMP) 中使用 Protobuf
kotlin
alexhilton9 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
lhDream9 天前
Kotlin 开发者必看!JetBrains 开源 LLM 框架 Koog 快速上手指南(含示例)
kotlin