Kotlin-类和对象

article/2025/8/5 12:53:52

文章目录

    • 主构造函数
    • 次要构造函数
    • 总结
  • 对象
    • 初始化
  • 类的继承
  • 成员函数
  • 属性覆盖(重写)
    • 智能转换
  • 类的扩展

class Student {
}

这是一个类,表示学生,怎么才能给这个类添加一些属性(姓名,年龄…)呢?

主构造函数

我们需要指定类的构造函数。构造函数也是函数的一种,但是它专门用于对象的创建
Kotlin的类可以添加一个主构造函数和一个或多个次要构造函数。主构造函数是类定义的一部分,像下面这样编写:

class Student constructor(name: String, age: Int) {}

如果主构造函数没有任何注释或可见性修饰符,则可以省略constructor关键字,如果类中没有其他内容要写,也可以直接省略{}, 像下面这样:

class Student (name: String, age: Int)

但是,这里仅仅是定义了构造函数的参数,还不是类的属性

仅在对象初始化时使用构造函数参数,并且不想让这些参数以属性的形式被外部访问时,可采用这种写法,那我们要怎么才能定义类的属性呢?

可以为这些参数添加var(可变)或val(不可变)关键字来表示属性:

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

这样才算是定义了类的属性,我们也可以给这些属性设置初始值:

class Student (var name: String = "zhangsan",val age: Int = 20)

因为是构造函数, 实例化的时候会传入值, 所以可以给也可以不给初始值

如果将这些属性直接作为类的成员变量写到类里面,必须配初始值,否则无法通过编译,这样我们不用编写主构造函数也能定义属性,这里仍会隐式生成一个无参的构造函数:

class Student  {var name: String = "zhangsan"val age: Int = 20
}

也可以写成这样:

class Student(name : String, age: Int) {var name: String = nameval age: Int = age
}

这样不是多此一举嘛,直接在括号里(主构造函数)定义属性不就好了,这样的好处就是可以自定义属性的getset方法

class Shape(var width:Int, var height:Int) {val area: Int
//      get() {
//          return width * height
//      }get() = width * height
}fun main() {var a = Shape(2, 3)println("Width: ${a.width}, Height: ${a.height}")println("Area: ${a.area}")
}

在这里插入图片描述

class Shape(width:Int, height:Int) {val area: Intinit {area = width * height}val perimeter: Intinit {perimeter = (width + height) * 2}
}

当然,如果不希望一开始就有初始值,而是之后某一个时刻(用它之前)去设定初始值, 我们也可以为其添加懒加载(用它之前给属性初始值):

lateinit 修饰符:

  1. 只能用于可变属性(即 var 声明的属性),而不能用于只读属性(使用 val 声明的属性)
  2. 不能用于基本数据类型的属性。
class Student {lateinit var name: Stringval age: Int = 0
}

次要构造函数

除了直接使用主构造函数创建对象外,我们也可以添加一些次要构造函数,次要构造函数中的参数仅仅表示传入的参数,不能像主构造函数那样定义属性:

  • 如果该类有一个主构造函数,则每个次要构造函数都需要直接或间接委托给主构造函数。委托到同一个类的另一个构造函数是 this 关键字完成的
class Student(var name: String, var age: Int) {//这里的 this 表示当前这个类, this() 就是调用当前类的无参构造函数//这里其实是调用主构造函数,并且参数只有name,年龄直接给默认值18constructor(name: String) : this(name, 18) {println("次要构造函数")}
}fun main() {var stu = Student("小明")
}

在这里插入图片描述

  • 如果一个类没有主构造函数,那么我们也可以直接在类中编写次要构造函数,:
class Student {var name: Stringvar age: Int//在次要构造函数的参数前使用 var 或者 val 是不被允许的//次要构造函数可以编写自定义的函数体constructor(name: String, age: Int) { //这里的参数不是类属性,仅仅是形参this.name = namethis.age = age}
}

总结

  • 主构造函数:可以直接定义类属性,使用更方便,但主构造函数只能存在一个,并且无法编写函数体,不过可以用init编写,下面对象初始化有介绍
  • 次要(辅助)构造函数:可以存在多个,并且可以自定义函数体,但是无法像主构造函数那样定义类属性,并且当类具有主构造函数时,所有的次要构造函数必须直接或间接地调用主构造函数

对象

构造函数也是函数,我们可以用类名()的形式创建对象

class Student(var name: String, var age: Int)fun main() {var stu: Student = Student("小明", 18)println(stu.name)println(stu.age)stu.name = "小红"println(stu.name)
}

在这里插入图片描述

初始化

在创建对象时,我们可能需要做一些初始化工作,可以使用初始化代码块(使用init关键字)来完成。假如我们希望对象在创建时, 年龄不足18岁就设定为18岁:

注意:我们在创建对象的时候,就会自动执行init里面的代码

class Student (var name: String, var age: Int){init {println("我是初始化操作")if (age < 18) age = 18}
}fun main() {var stu = Student("小明", 2)println(stu.age)
}

在这里插入图片描述
初始化操作可以有很多个:

class Student (var name: String, var age: Int){//多个初始化操作时,按从上往下的顺序执行init {if (age < 18) age = 18}init {age = 20}
}fun main() {var stu = Student("小明", 2)println(stu.age)
}

在这里插入图片描述
如果初始化代码要用成员属性, 那就要在用之前先赋值

class Student {init {println("name is $name")println("age is $age")}var name: String = "Student"var age: Int = 20
}fun main() {var stu = Student()
}

在这里插入图片描述
这里需要注意一下,次要构造函数实际上需要先执行主构造函数,所以会优先执行init

class Student (var name: String, var age: Int){init {println("我是初始化代码块")}constructor(name: String) : this(name, 18){println("我是次要构造函数的语句")}
}fun main() {var stu = Student("张三")
}

在这里插入图片描述

类的继承

在Kotlin中, 默认情况下, 类是"终态"的(不能被任何类继承)。要使类可继承,要用open关键字标记需要被继承的类

在Kotlin中只能单继承。需要注意的是,在对象创建并初始化的时候, 会优先对父类进行初始化,再对子类进行初始化

open class Student {init {println("父类初始化")}fun hello() = println("打招呼")
}class ArtStudent: Student() {init {println("子类初始化")}fun draw() = println("我会画画")
}fun main() {val student = ArtStudent()student.draw()student.hello()
}

在这里插入图片描述

成员函数

现在我们的类有了属性,而对象也可以做出一些行为,我们可以通过定义函数来实现

class Student(var name: String, var age: Int) {fun hello() {println("大家好, 我是$name")}
}fun main() {val student = Student("路飞", 20)student.hello()
}

在这里插入图片描述
如果函数中的变量存在歧义,那么优先使用作用域最近的一个

class Student(var name: String, var age: Int) {fun hello(name: String)  {println("大家好, 我是$name")}
}fun main() {Student("路飞", 20).hello("比企谷八幡")
}

在这里插入图片描述
如果我们需要获取的是类中的成员属性,需要使用this关键字来表示

class Student(var name: String, var age: Int) {fun hello(name: String)  {println("大家好, 我是${this.name}")}
}fun main() {Student("路飞", 20).hello("比企谷八幡")
}

在这里插入图片描述

属性覆盖(重写)

有些时候,我们希望子类继承父类的某些属性,但是又希望去修改这些属性的默认实现

我们可以使用 override 关键字表示对一个属性的覆盖(重写)

open class Student {// 函数必须添加open关键字才能被子类覆盖open fun hello() = println("打招呼")
}class ArtStudent: Student() {// 在子类中重写方法要添加 override 关键字override fun hello() {println("呀哈喽")super.hello()}
}fun main() {val artStudent = ArtStudent()artStudent.hello()
}

在这里插入图片描述
同样的, 类的某个变量也是可以进行覆盖的

open class Student {open val name: String = "小明"
}class ArtStudent: Student() {override val name: String = "小红"
}fun main() {val artStudent = ArtStudent()println(artStudent.name)
}

在这里插入图片描述
对于可变的变量,也可以这样不加open

open class Student {var name: String = "小明"
}class ArtStudent: Student() {init {name = "小红"}
}fun main() {val artStudent = ArtStudent()println(artStudent.name)
}

在这里插入图片描述
也可以在子类的主构造函数中直接覆盖

open class Student {open var name: String = "小明"
}class ArtStudent(override var name: String): Student()fun main() {val artStudent = ArtStudent("小王")println(artStudent.name)
}

在这里插入图片描述

open class Student {open var name: String = "小明"
}class ArtStudent(override var name: String): Student() {init {name = "小红"}
}fun main() {val artStudent = ArtStudent("小王")println(artStudent.name)
}

在这里插入图片描述
这种初始化顺序要特别注意

open class Student {open var name: String = "小明"init {println(name.length) // 这里拿到的name其实是还未初始化的子类的name}
}class ArtStudent(override var name: String): Student()fun main() {val artStudent = ArtStudent("小王")println(artStudent.name)
}

由于父类初始化在子类之前,此时子类还没有初始化,其覆盖的属性此时没有值,在JVM平台下,没有初始化的对象引用默认为null,那么这里就会出现空指针异常

在这里插入图片描述
name明明是一个不可空的String类型,还会出现空指针异常。因此,对于使用了open关键字的属性只要是在初始化函数、构造函数中使用,要额外小心

智能转换

编译器可以根据当前的语境自动进行类型转换
在这里插入图片描述
不仅仅是if判断的场景,还包括when、while 以及 && || 等

open class Studentclass ArtStudent: Student() {fun draw(): Boolean = true
}fun main() {val student: Student = ArtStudent()while (student is ArtStudent) student.draw()// 很明显如果前面为真,那么肯定是 ArtStudent 类型, 后面可以智能转换if (student is ArtStudent && student.draw()) ;
}

可空类型同样支持这样的智能转换

class Student {fun hello() = println("Hello World")
}fun main() {val student: Student? = Student()student?.hello()if (student != null) {student.hello() //根据语境将student从Student?智能转换为Student}
}

在处理可空类型时,为了防止出现异常,我们可以使用更加安全的as?运算符

open class Studentclass ArtStudent: Student()fun main() {val student: Student? = nullstudent as? ArtStudent //当student为null时,不会抛出异常,而是返回null
}

类的扩展

Kotlin提供了扩展类或接口的操作来为其添加额外的函数或属性,无需通过类继承或者使用装饰器等设计模式

比如我们想为String类型添加一个自定义操作

fun String.test() = "hello world"fun main() {val str = ""println(str.test())
}

在这里插入图片描述
注意,类的扩展是静态的,实际上并不会修改原本的类,也不会将新成员插入到类中,仅仅是将我们定义的功能变得可调用,像真的有一样。同时,在编译时也会明确具体调用的扩展函数:

open class Shapeclass Rectangle : Shape()fun Shape.getName() = "Shape"
fun Rectangle.getName() = "Rectangle"fun printShape(shape: Shape) {println(shape.getName())
}fun printRectangle(rectangle: Rectangle) {println(rectangle.getName())
}fun main() {printShape(Rectangle())printRectangle(Rectangle())
}

在这里插入图片描述

如果类本身就具有同名同参的函数,那么扩展函数将失效:

class Test {fun hello() = println("你干嘛")
}fun Test.hello() = println("哎呦")fun main() {Test().hello()
}

在这里插入图片描述
不过,重载是没问题的

class Test {fun hello() = println("你干嘛")
}fun Test.hello(str: String) = println(str)fun main() {Test().hello("哎呦")
}

在这里插入图片描述
同样的,类的属性也是可以通过这种形式来扩展的,但是有一些小小的要求
在这里插入图片描述
可以看到直接扩展属性是不允许的,扩展并不是真的往类中添加属性。因此, 扩展属性本质上也不会真的插入一个成员字段到类的定义中,这就导致并没有变量去存储我们的数据, 我们只能明确定义get和set来创建扩展属性

class Test val Test.nameget() = "666"fun main() {println(Test().name)
}

在这里插入图片描述

由于扩展属性并没有存储真正的变量,而是使用get和set函数,所以,像field这样的后备字段就无法使用了

还有需要注意的是,我们定义的扩展属性,同样受到访问权限控制

在这里插入图片描述
除了直接在顶层定义类的扩展外,我们也可以在类中定义其他类的扩展,并且在定义时可以直接使用其他类提供的属性

class A {val name = "张三"
}class B {//像这种扩展,由于是在类中定义,因此也仅限于类内部使用fun A.test() = println(this.name)fun test() = A().test()
}fun main() = B().test()

在这里插入图片描述
在函数名发生冲突的时候,需要特别处理

class A {fun hello() = println("Hello A")
}class B {//像这种扩展,由于是在类中定义,因此也仅限于类内部使用fun A.test() {hello() //优先匹配被扩展类里的函数this.hello()this@B.hello()}fun hello() = println("Hello B")fun test() = A().test()
}fun main() = B().test()

在这里插入图片描述
定义在类中的扩展也可以跟随类的继承结构,进行重写

open class A {open fun A.test() = "AAA"fun hello() = println(test())
}class B:A() {override fun A.test() = "BBB" //对父类定义的扩展函数进行重写
}fun main() {A().hello()B().hello()
}

在这里插入图片描述

我们可以在某个函数里面编写扩展,但作用域仅限于当前函数

fun main() {fun String.print() = println("此剑斩穹,不破不休")"".print()
}

还可以将一个扩展函数作为参数给到一个函数类型变量

fun main() {// func就是扩展函数名val func: String.(Int) -> String = { it.toString() + this }println("出击!".func(123))//如果是直接调用,那就必须传入对应类型的对象作为首个参数,此时this就指向我们传入的参数println(func("撤退!", 321)) 
}

在这里插入图片描述


http://www.hkcw.cn/article/ZKTGUCHSxC.shtml

相关文章

OpenWrt 配置 IOS 、 Android USB共享网络

前言 由于快过年了老家的宽带早已到期, 手机卡流量还有100G, 于是想到使用手机USB网络共享给openwrt来上网。 设备准备 准备手机( IOS / Android / USB随身Wi-Fi ) 带USB插口的openwrt路由器 openwrt配置 安装 kmod-usb-net-rndis、kmod-usb-storage 依赖包: 打开openwrt…

手把手教你在VSCode里开启中文模式(新手必看避坑指南)

文章目录 一、核心操作三步走&#xff08;有手就会&#xff09;1. 插件商店精准搜索2. 闪电安装法3. 终极验证 二、常见翻车现场急救指南场景1&#xff1a;插件装了但没反应场景2&#xff1a;菜单汉化不全场景3&#xff1a;终端/调试界面英文 三、高阶玩家必备骚操作1. 多语言自…

8个2025年必备的IntelliJ IDEA免费插件

IntelliJ IDEA 必备免费插件&#xff0c;全面提升 Java 开发效率与代码质量 微信搜索关注《Java学研大本营》 在 Java 开发中&#xff0c;IntelliJ IDEA 已然是一款强大的集成开发环境。但如果能搭配上合适的插件&#xff0c;能让开发体验提升到全新的高度。 今天给大家分享一…

Mac电脑(M芯片)安装ubuntu22.04

一、下载VMware虚拟机 VMware官网下载VMware Fusion 二、下载ubuntu镜像 M系列的Mac电脑要下载arm架构的镜像 方法一&#xff1a;官网下载 方法二&#xff1a;清华源下载 清华源镜像 点击获取下载链接 选择Ubuntu&#xff0c;下载22.04.5(arm64,Server) 三、创建虚拟机 …

Docker Desktop配置国内镜像源教程

在使用 Docker 时&#xff0c;由于默认镜像源在国外&#xff0c;经常会遇到下载速度慢、连接超时等问题。本文将详细介绍如何在 Windows 系统中为 Docker 配置国内镜像源&#xff0c;以提升镜像拉取速度。 常用国内镜像源 https://docker.1ms.run清华镜像源 https://docker.m…

Python详细安装教程——Python及PyCharm超详细安装教程:新手小白也能轻松搞定!(最新版)

Python作为一门简单易学、功能强大的编程语言&#xff0c;近年来在数据分析、人工智能、Web开发等领域广受欢迎。而PyCharm作为一款专业的Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;提供了强大的代码编辑、调试和项目管理功能&#xff0c;是Python开发者的得力…

2024年最新版IntelliJ IDEA下载安装过程(含Java环境搭建)

1.摘要 本文介绍了2024年最新版IntelliJ IDEA的下载和安装过程&#xff0c;包括IntelliJ IDEA介绍、Java和JDK的介绍、如何选择社区版和商业版、Java环境的搭建、讲解了JDK的下载安装及配置。同时&#xff0c;文章还简要概述了Java语言的特点和适用场景&#xff0c;是Java初学…

Windows docker下载minio出现“Using default tag: latestError response from daemon”

Windows docker下载minio出现 Using default tag: latest Error response from daemon: Get "https://registry-1.docker.io/v2/": context deadline exceeded 此类情况&#xff0c;一般为镜像地址问题。 {"registry-mirrors": ["https://docker.re…

JetBrains 开发工具弹窗 We could not validate your license 包括但不限于IDEA/CLion/GoLand/PyCharm

JetBrains 开发工具弹窗 We could not validate your license 包括但不限于IDEA/CLion/GoLand/PyCharm 关于 JetBrains 开发工具近期更新后&#xff0c;始终弹窗 We could not validate your license的问题处理 其实问题很简单&#xff0c;弹窗的用户其实是因为地区中选择了中国…

Python深度学习环境配置(Pytorch、CUDA、cuDNN),包括Anaconda搭配Pycharm的环境搭建以及基础使用教程(保姆级教程,适合小白、深度学习零基础入门)

全流程导览 一、前言二、基本介绍2.1全过程软件基本介绍2.1.1 Pytorch2.1.2 Anaconda2.1.3 Pycharm2.1.4 显卡GPU及其相关概念2.1.5 CUDA和cuDNN 2.2 各部分相互间的联系和安装逻辑关系 三、Anaconda安装3.1安装Anaconda3.2配置环境变量3.3检验是否安装成功 四、Pycharm安装五、…

从0开始的github学生认证并使用copilot教程(超详细!)

目录 一.注册github账号 1.1、仅仅是注册 1.2、完善你的profile 二、Github 学生认证 邮箱 学校名称 How do you plan to use Github? Upload Proof 学校具体信息 一.注册github账号 1.1、仅仅是注册 1.用如QQ邮箱的第三方邮箱注册github 再添加.edu结尾的教育邮箱&…

黑马Java面试笔记之MySQL篇(优化)

一. 慢查询 在MySQL中&#xff0c;如何定位慢查询&#xff1f; 出现慢查询的情况有以下几种&#xff1a; 聚合查询多表查询表数据量过大查询深度分页查询 表象&#xff1a;页面加载过慢&#xff0c;接口压测响应时间过长&#xff08;超过1s&#xff09; 1.2 如何定位慢查询&…

批量导出CAD属性块信息生成到excel——CAD C#二次开发(插件实现)

本插件可实现批量导出文件夹内大量dwg文件的指定块名的属性信息到excel&#xff0c;效果如下&#xff1a; 插件界面&#xff1a; dll插件如下&#xff1a; 使用方法&#xff1a; 1、获取此dll插件。 2、cad命令行输入netload &#xff0c;加载此dll&#xff08;要求AutoCAD&…

Chrome 通过FTP,HTTP 调用 Everything 浏览和搜索本地文件系统

【提问1】 Chrome调用本地 everything.exe, everything 好像有本地 FTP 服务器&#xff1f; 【DeepSeek R1 回答】 是的&#xff0c;Everything 确实内置了 HTTP/FTP 服务器功能&#xff0c;这提供了一种相对安全的浏览器与本地应用交互的方式。以下是完整的实现方案&#x…

《汇编语言》第13章 int指令

中断信息可以来自 CPU 的内部和外部&#xff0c;当 CPU 的内部有需要处理的事情发生的时候&#xff0c;将产生需要马上处理的中断信息&#xff0c;引发中断过程。在第12章中&#xff0c;我们讲解了中断过程和两种内中断的处理。 这一章中&#xff0c;我们讲解另一种重要的内中断…

CFTel:一种基于云雾自动化的鲁棒且可扩展的远程机器人架构

中文标题&#xff1a; CFTel&#xff1a;一种基于云雾自动化的鲁棒且可扩展的远程机器人架构 英文标题&#xff1a; CFTel: A Practical Architecture for Robust and Scalable Telerobotics with Cloud-Fog Automation 作者信息 Thien Tran, Jonathan Kua, Minh Tran, Hongh…

Spring Boot是什么?

Spring Boot是什么&#xff1f; Spring Boot是什么&#xff1f;1. 引言1.1 什么是Spring Boot&#xff1f;1.2 为什么选择Spring Boot&#xff1f; 2. Spring Boot的起源2.1 Spring框架的历史2.2 Spring Boot的诞生背景 3. Spring Boot的核心特性3.1 快速启动和部署3.2 自动配置…

基于大数据爬虫+Python+数据可视化大屏的慧游数据爬虫与推荐分析系统(源码+论文+PPT+部署文档教程等)

博主介绍&#xff1a;CSDN毕设辅导第一人、全网粉丝50W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringB…

实时响应的秘密:用Spring Boot轻松实现流式AI输出

1、背景 随着AI的快速发展&#xff0c;越来越多的AI应用诞生了&#xff0c;但是AI也有响应慢的问题&#xff0c;一般不能够即时响应&#xff0c;为了优化用户体验&#xff0c;现在大部分AI应用都是实现了打字机的效果&#xff0c;那么这种效果是如何实现的呢&#xff1f;今天我…

Spring Boot 中 RabbitMQ 的使用

目录 引入依赖 添加配置 Simple&#xff08;简单模式&#xff09; 生产者代码 消费者代码 ​编辑 Work Queue&#xff08;工作队列&#xff09; 生产者代码 消费者代码 Publish/Subscribe&#xff08;发布/订阅&#xff09; 生产者代码 消费者代码 Routing&#x…