首页 > 其他分享 >【Kotlin】类和对象

【Kotlin】类和对象

时间:2024-04-07 22:12:17浏览次数:26  
标签:name 对象 Kotlin age println Student var 构造函数

1 前言

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

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

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

img

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

package com.zhyan8.kotlinStudy

class Student {
}

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

2 类的结构

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

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 节中会介绍主构造函数。

​ 运行程序后,打印如下。

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 无参主构造函数

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")
    }
}

​ 运行程序后,打印如下。

init
-----------------------------------------
init
constructor, name=zhang san

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

class Student private constructor() {
    ...
}

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

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")
    }
}

​ 运行程序后,打印如下。

init, name=xiao ming, age=23
-----------------------------------------
init, name=zhang san, age=18
constructor

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

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 修饰。

​ 运行程序后,打印如下。

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 前面添加权限修饰符。

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 类是所有类的基类。

​ 类的初始化顺序如下。

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

5.1 子类无主构造函数

fun main() {
    var stu = Student("zhang san", 23, 1001)
}

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

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

class Student : People {
    init {
        println("Student init, name=$name") // 3 (此处不能访问age和id)
    }

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

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

​ 运行程序后,打印如下。

People init, name=zhang san
People constructor, name=zhang san, age=23
Student init, name=zhang san
Student constructor, name=zhang san, age=23, id=1001

5.2 子类有主构造函数

fun main() {
    var stu = Student("zhang san", 23, 1001)
}

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

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

class Student(name: String, var age: Int) : People(name, age) {
    init {
        println("Student init, name=$name, age=$age") // 3 (此处不能访问id)
    }

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

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

​ 运行程序后,打印如下。

People init, name=zhang san
People constructor, name=zhang san, age=23
Student init, name=zhang san, age=23
Student constructor, name=zhang san, age=23, id=1001

6 多态

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

6.1 覆盖函数

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")
    }
}

​ 运行程序后,打印如下。

People init, name=li si, age=25
Student init, name=li si, age=25, id=1002
Student say

6.2 覆盖属性

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"
}

​ 运行程序后,打印如下。

doSomething, name=li si

6.3 类型智能转换

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

7 抽象类

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

fun main() {
    // var peo = People() // 编译报错, 抽象类不能被实例化
    var stu = Student()
    stu.say()
}

abstract class People {
    abstract var name: String
    abstract fun say()
}

class Student : People() {
    override var name: String = "xiao min"

    override fun say() {
        println("$name: Hello")
    }
}

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

​ 运行程序后,打印如下。

xiao min: Hello

8 接口

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

fun main() {
    var c = C("xxx", "yyy")
    c.aFun()
    c.bFun()
}

interface A {
    var x: String
    fun aFun()
}

interface B {
    var y: String
    fun bFun()
}

class C(override var x: String, override var y: String) : A, B {
    override fun aFun() {
        println("aFun, x=$x")
    }

    override fun bFun() {
        println("bFun, y=$y")
    }
}

​ 运行程序后,打印如下。

aFun, x=xxx
bFun, y=yyy

9 枚举类型

1)枚举类

enum class Color(val tag: String) {
    RED("red") {
        override fun test() {
            println("test, $tag")
        }
    },
    GREEN("green") {
        override fun test() {
            println("test, $tag")
        }
    },
    BLUE("blue") {
        override fun test() {
            println("test, $tag")
        }
    };

    fun printColor(): Unit {
        println("color=$tag")
    }

    abstract fun test()
}

2)enumValueOf、enumValues

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

3)name、ordinal、entries、values

fun main() {
    println(Color.GREEN.name) // GREEN
    println(Color.RED.ordinal) // 0
    var entries: EnumEntries<Color> = Color.entries
    println(entries) // [RED, GREEN, BLUE]
    var colors: Array<Color> = Color.values()
    println(colors.joinToString()) // RED, GREEN, BLUE
}

4)自定义属性和函数

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

10 data class

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

  • equals()
  • hashCode()
  • toString()
  • componentN()
  • copy()
fun main() {
    val stu1 = Student("Zhang", 20)
    val stu2 = Student("Zhang", 20)
    // hashCode、equals
    println(stu1 == stu2) // true
    // toString
    println(stu1) // Student(name=Zhang, age=20)
    // componentN
    var (name, age) = stu1
    println("($name, $age)") // (Zhang, 20)
    // copy
    var stu3 = stu1.copy()
}

data class Student(var name: String, var age: Int)

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

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

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

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

​ 声明:本文转自【Kotlin】类和对象

标签:name,对象,Kotlin,age,println,Student,var,构造函数
From: https://www.cnblogs.com/zhyan8/p/18115114

相关文章

  • 【SQL】数据定义语言(DDL):包括创建、修改和删除数据库对象
    数据定义语言(DDL)是用于定义和管理数据库中的数据结构和对象的SQL语言子集。它允许用户创建、修改和删除数据库中的表、索引、触发器、序列、存储过程等对象。DDL语句在数据库系统中执行时,通常会影响整个数据库的结构,而不是单个的数据记录。以下是DDL中一些常用语句的详细介绍......
  • NET领域性能最好的对象映射框架Mapster使用方法
    Mapster是一个开源的.NET对象映射库,它提供了一种简单而强大的方式来处理对象之间的映射。在本文中,我将详细介绍如何在.NET中使用Mapster,并提供一些实例和源代码。和其它框架性能对比:Mapster的安装和配置:首先,打开VisualStudio并创建一个新的.NET项目。在NuGet包管理器控制台......
  • C++中的类与对象丶this指针和构造函数与析构函数 (一)
    C++中的类与对象和this指针(一)一丶类与对象1.类的引入2.类的实例化3.类的类型的大小I.计算类或对象的大小II.规定空类占一个字节大小4.类中的访问权限5.类中的构造函数和析构函数I.构造函数II.析构函数二丶this指针1.this指针的引出2.this指针的特性3.th......
  • 【沈阳航空航天大学】 <C++ 类与对象计分作业>
    C++类与对象1.设计用类完成计算两点距离2.设计向量类3.求n!4.出租车收费类的设计与实现5.定义并实现一个复数类6.线性表类的设计与实现7.数组求和8.数组求最大值1.设计用类完成计算两点距离【问题描述】设计二维点类Point,包括私有成员:横坐标x,纵坐标y。能够......
  • 面对对象编程(四)
    面对对象编程(四)static补充1.静态变量及方法packageopp.demo09;//static:publicclassStudent{privatestaticintage;//静态变量privatedoublescore;//非静态变量publicvoidrun(){}publicstaticvoidgo(){}public......
  • 面向对象设计原则实验之“合成复用原则”
    优先使用对象组合,而不是通过继承来达到复用的目的。某软件公司开发人员在初期的CRM(客户关系管理)系统设计中考虑到客户数量不多。系统采用Access作为数据库,与数据库操作有关的类(例如CustomerDAO类等)都需要连接致据库,连接数据库的方法getConnection()封装在DBUtil类......
  • 前端获取链接网址key值对象,访问的设备型号等
    有时候我们业务需要拿到页面跳转过去附带的key值对象以及用户设备型号,用以业务订购或其它操作。例如跳转到业务链接为:http://localhost:3000/?adid=__AID__&creativeid=__CID__&creativetype=__CTYPE__&clickid=__CLICKID__我们需要用到adid、clickid等以上值并获取到用户的......
  • SWEN20003面向对象软件开发项目
    SWEN20003面向对象软件开发项目1,2024墨尔本大学计算机与信息系统学院SWEN20003面向对象软件开发项目1,2024年第1学期发布时间:2024年3月25日星期一,美国东部时间晚上11:30首次提交截止时间:2024年3月28日星期四,美国东部时间晚上11:30项目截止时间:2024年4月17日星期三上午11:30在开......
  • Oracle 获取某个包依赖的所有对象包括其子对象
     Demo ---创建表createtablecux_common_imports_temp(attribute1varchar2(2000),attribute2varchar2(2000));declare--获取相应的某个程序包所需要应用的包cursorp_cur(p_namevarchar2)isselectdd.name,dd.......
  • 在使用set添加对象的时候,重写了hashcode方法后,为什么equals返回的仍是false,如何理解ha
    /**1.对象的哈希码值和内存地址值不是一回事;*2.如果自定义类不复写Object类中的equals方法,那么equals比较的就是两个对象的内存地址值。*//***下面证明了hash值和内存地址的关系*一、当没有重写hashCode()方法的时候,*S......