当前位置:   article > 正文

【Kotlin】类和对象_kotlin 类和对象

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 文件。

  1. package com.zhyan8.kotlinStudy
  2. class Student {
  3. }

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

2 类的结构

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

  1. fun main() {
  2. var stu1 = Student()
  3. stu1.study()
  4. println("-----------------------------------------")
  5. var stu2 = Student("li si", 23)
  6. }
  7. class Student {
  8. private var name: String = "zhang san" // 属性
  9. get() { // name的getter函数
  10. return field
  11. }
  12. set(value) { // name的setter函数
  13. field = value
  14. }
  15. private var age: Int = 18 // 属性
  16. init { // 初始化代码块, 在构造函数前执行
  17. println("Student init, name=$name, age=$age")
  18. }
  19. constructor() { // 无参构造函数
  20. println("create-1, name=$name, age=$age")
  21. }
  22. constructor(name: String, age: Int) { // 有参构造函数
  23. println("create-2, name=$name, age=$age")
  24. this.name = name
  25. this.age = age
  26. }
  27. fun study() { // 成员函数
  28. println("study...")
  29. }
  30. }

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

        运行程序后,打印如下。

  1. Student init, name=zhang san, age=18
  2. create-1, name=zhang san, age=18
  3. study...
  4. -----------------------------------------
  5. Student init, name=zhang san, age=18
  6. create-2, name=li si, age=23

        类的初始化顺序如下:

        父类静态代码块(companion object)→ 子类静态代码块(companion object)→ 父类属性初始化 → 父类 init 代码块 → 父类构造函数 → 子类属性初始化 → 子类 init 代码块 → 子类构造函数。

3 主构造函数

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

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

3.1 无参主构造函数

  1. fun main() {
  2. var stu1 = Student()
  3. println("-----------------------------------------")
  4. var stu2 = Student("zhang san")
  5. }
  6. class Student() { // 等价与: class Student constructor()
  7. init { // 初始化代码块, 在构造函数前执行
  8. println("init")
  9. }
  10. constructor(name: String): this() {
  11. println("constructor, name=$name")
  12. }
  13. }

        运行程序后,打印如下。

  1. init
  2. -----------------------------------------
  3. init
  4. constructor, name=zhang san

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

  1. class Student private constructor() {
  2. ...
  3. }

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

  1. fun main() {
  2. var stu1 = Student("xiao ming", 23)
  3. println("-----------------------------------------")
  4. // stu1.name // 编译报错, name不是成员属性
  5. var stu2 = Student()
  6. }
  7. class Student(name: String, age: Int) {
  8. init {
  9. println("init, name=$name, age=$age")
  10. }
  11. constructor(): this("zhang san", 18) {
  12. println("constructor")
  13. }
  14. }

        运行程序后,打印如下。

  1. init, name=xiao ming, age=23
  2. -----------------------------------------
  3. init, name=zhang san, age=18
  4. constructor

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

  1. fun main() {
  2. var stu1 = Student("xiao ming", 23)
  3. println("stu1.name=${stu1.name}, stu1.age=${stu1.age}")
  4. println("-----------------------------------------")
  5. var stu2 = Student()
  6. println("stu2.name=${stu2.name}, stu2.age=${stu2.age}")
  7. }
  8. class Student(var name: String, var age: Int) {
  9. init {
  10. println("init, name=$name, age=$age")
  11. }
  12. constructor(): this("zhang san", 18) {
  13. println("constructor")
  14. }
  15. }

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

        运行程序后,打印如下。

  1. init, name=xiao ming, age=23
  2. stu1.name=xiao ming, stu1.age=23
  3. -----------------------------------------
  4. init, name=zhang san, age=18
  5. constructor
  6. stu2.name=zhang san, stu2.age=18

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

  1. class Student(private val name: String, protected var age: Int) {
  2. ...
  3. }

4 封装

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

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

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

5 继承

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

        类的初始化顺序如下。

  1. 父类主构造函数
  2. 父类 init 代码块
  3. 父类次要构造函数
  4. 子类主构造函数
  5. 子类 init 代码块
  6. 子类次要构造函数

5.1 子类无主构造函数

  1. fun main() {
  2. var stu = Student("zhang san", 23, 1001)
  3. }
  4. open class People(var name: String) {
  5. init {
  6. println("People init, name=$name") // 1
  7. }
  8. constructor(name: String, age: Int): this(name) {
  9. println("People constructor, name=$name, age=$age") // 2
  10. }
  11. }
  12. class Student : People {
  13. init {
  14. println("Student init, name=$name") // 3 (此处不能访问age和id)
  15. }
  16. constructor(name: String, age: Int, id: Int) : super(name, age) {
  17. println("Student constructor, name=$name, age=$age, id=$id") // 4
  18. }
  19. }

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

        运行程序后,打印如下。

  1. People init, name=zhang san
  2. People constructor, name=zhang san, age=23
  3. Student init, name=zhang san
  4. Student constructor, name=zhang san, age=23, id=1001

5.2 子类有主构造函数

  1. fun main() {
  2. var stu = Student("zhang san", 23, 1001)
  3. }
  4. open class People(var name: String) {
  5. init {
  6. println("People init, name=$name") // 1
  7. }
  8. constructor(name: String, age: Int): this(name) {
  9. println("People constructor, name=$name, age=$age") // 2
  10. }
  11. }
  12. class Student(name: String, var age: Int) : People(name, age) {
  13. init {
  14. println("Student init, name=$name, age=$age") // 3 (此处不能访问id)
  15. }
  16. constructor(name: String, age: Int, id: Int): this(name, age) {
  17. println("Student constructor, name=$name, age=$age, id=$id") // 4
  18. }
  19. }

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

        运行程序后,打印如下。

  1. People init, name=zhang san
  2. People constructor, name=zhang san, age=23
  3. Student init, name=zhang san, age=23
  4. Student constructor, name=zhang san, age=23, id=1001

6 多态

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

6.1 覆盖函数

  1. fun main() {
  2. var peo: People = Student("li si", 25, 1002)
  3. peo.say()
  4. }
  5. open class People(var name: String, var age: Int) {
  6. init {
  7. println("People init, name=$name, age=$age")
  8. }
  9. open fun say() {
  10. println("People say")
  11. }
  12. }
  13. class Student(name: String, age: Int, var id: Int) : People(name, age) {
  14. init {
  15. println("Student init, name=$name, age=$age, id=$id")
  16. }
  17. override fun say() {
  18. println("Student say")
  19. }
  20. }

         运行程序后,打印如下。

  1. People init, name=li si, age=25
  2. Student init, name=li si, age=25, id=1002
  3. Student say

6.2 覆盖属性

  1. fun main() {
  2. var peo : People = Student()
  3. peo.doSomething()
  4. }
  5. open class People {
  6. open var name: String = "zhang san"
  7. fun doSomething() {
  8. println("doSomething, name=$name")
  9. }
  10. }
  11. class Student : People() {
  12. override var name: String = "li si"
  13. }

        运行程序后,打印如下。

doSomething, name=li si

6.3 类型智能转换

  1. fun main() {
  2. var peo: People = Student()
  3. // peo.study() // 编译报错
  4. if (peo is Student) {
  5. peo.study() // 智能转换为Student
  6. }
  7. }
  8. open class People {
  9. }
  10. class Student : People() {
  11. fun study() {
  12. println("study...")
  13. }
  14. }

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

7 抽象类(abstract class)

        使用 abstract 修饰的类称为抽象类,抽象类中可以有抽象属性和函数,这些属性和函数被添加了 abstract 修饰符,父类不能实现,子类必须重写实现(子类如果也是抽象类除外)。抽象类不能被实例化,只能实例化其具化子类,抽象类中允许有具化的属性和函数。

  1. fun main() {
  2. // var peo = People() // 编译报错, 抽象类不能被实例化
  3. var stu = Student()
  4. stu.say()
  5. }
  6. abstract class People {
  7. abstract var name: String
  8. abstract fun say()
  9. }
  10. class Student : People() {
  11. override var name: String = "xiao min"
  12. override fun say() {
  13. println("$name: Hello")
  14. }
  15. }

        说明:Java 中只有抽象函数,没有抽象属性。 

        运行程序后,打印如下。

xiao min: Hello

8 接口(interface)

        接口与抽象类有些类似,接口里只有抽象属性和函数(函数允许有默认实现,属性不能),Kotlin 中允许一个类实现多个接口,但最多只能继承一个类。

  1. fun main() {
  2. var c = C("xxx", "yyy")
  3. c.aFun()
  4. c.bFun()
  5. }
  6. interface A {
  7. var x: String
  8. fun aFun()
  9. }
  10. interface B {
  11. var y: String
  12. fun bFun()
  13. }
  14. class C(override var x: String, override var y: String) : A, B {
  15. override fun aFun() {
  16. println("aFun, x=$x")
  17. }
  18. override fun bFun() {
  19. println("bFun, y=$y")
  20. }
  21. }

        运行程序后,打印如下。

  1. aFun, x=xxx
  2. bFun, y=yyy

9 枚举类型(enum class)

        1)枚举类

  1. enum class Color(val tag: String) {
  2. RED("red") {
  3. override fun test() {
  4. println("test, $tag")
  5. }
  6. },
  7. GREEN("green") {
  8. override fun test() {
  9. println("test, $tag")
  10. }
  11. },
  12. BLUE("blue") {
  13. override fun test() {
  14. println("test, $tag")
  15. }
  16. };
  17. fun printColor(): Unit {
  18. println("color=$tag")
  19. }
  20. abstract fun test()
  21. }

        2)enumValueOf、enumValues

  1. fun main() {
  2. var color: Color = enumValueOf<Color>("RED")
  3. var colors: Array<Color> = enumValues<Color>()
  4. println(colors.joinToString()) // RED, GREEN, BLUE
  5. }

        3)name、ordinal、entries、values

  1. fun main() {
  2. println(Color.GREEN.name) // GREEN
  3. println(Color.RED.ordinal) // 0
  4. var entries: EnumEntries<Color> = Color.entries
  5. println(entries) // [RED, GREEN, BLUE]
  6. var colors: Array<Color> = Color.values()
  7. println(colors.joinToString()) // RED, GREEN, BLUE
  8. }

        4)自定义属性和函数

  1. fun main() {
  2. Color.RED.printColor() // color=red
  3. Color.GREEN.test() // test, green
  4. println(Color.BLUE.tag) // blue
  5. }

10 数据类(data class)

        在 class 前面添加 data 关键字表示为一个数据类,编译器会根据主构造函数中声明的所有属性自动为其生成以下函数。

  • equals()
  • hashCode()
  • toString()
  • componentN()
  • copy()
  1. fun main() {
  2. val stu1 = Student("Zhang", 20)
  3. val stu2 = Student("Zhang", 20)
  4. // hashCode、equals
  5. println(stu1 == stu2) // true
  6. // toString
  7. println(stu1) // Student(name=Zhang, age=20)
  8. // componentN
  9. var (name, age) = stu1
  10. println("($name, $age)") // (Zhang, 20)
  11. // copy
  12. var stu3 = stu1.copy()
  13. }
  14. data class Student(var name: String, var age: Int)

        为了确保生成代码的一致性和有效性,数据类必须满足以下要求。

  • 主构造函数中至少有一个参数。
  • 主构造函数中的参数必须标记为 val 或 var。
  • 数据类不能是抽象的、开放的、封闭的、内部的。

        数据类中成员函数的生成遵循以下规则。

  • 如果数据类中,equals、hashCode、toString 等函数存在显示实现,或者在父类中有 final 实现,则不会自动生成这些函数,并使用现有的实现。
  • 如果父类具有 open、componentN 函数,并返回兼容类型,则为数据类生成相应的函数,并覆盖父类的相应函数。
  • 不允许为 componentN 和 copy 函数提供显示实现。

        componentN 函数的介绍见 → 运算符函数、解构函数、中缀函数

11 内部类(inner class)

        内部类是指在类内部申明的类,并且在 class 前面添加了 inner 申明。

  1. fun main() {
  2. var a = A()
  3. var b = a.B()
  4. b.test()
  5. }
  6. class A {
  7. private var str1 = "A-abc"
  8. private var str2 = "A-xyz"
  9. private fun fun1() {
  10. println("A-fun1")
  11. }
  12. private fun fun2() {
  13. println("A-fun2")
  14. }
  15. inner class B {
  16. private var str1 = "B-abc"
  17. private fun fun1() {
  18. println("B-fun1")
  19. }
  20. fun test() {
  21. println(str1) // B-abc
  22. println(this@A.str1) // A-abc
  23. println(str2) // A-xyz
  24. fun1() // B-fun1
  25. this@A.fun1() // A-fun1
  26. fun2() // A-fun2
  27. }
  28. }
  29. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/992488
推荐阅读
相关标签
  

闽ICP备14008679号