1 前言

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

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

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

​ 如下,创建了一个 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】类和对象

【Kotlin】类和对象的更多相关文章

  1. Kotlin 类和对象

    类定义 Kotlin 类可以包含:构造函数和初始化代码块.函数.属性.内部类.对象声明. Kotlin 中使用关键字 class 声明类,后面紧跟类名: class Runoob { // 类名为 R ...

  2. Kotlin语法(类和对象)

    二.类和对象: 1. 类定义: 类的声明包含类名,类头(指定类型参数,主构造函数等等),以及类主体,用大括号包裹.类头和类体是可选的:如果没有类体可以省略大括号. class Invoice{ } 2 ...

  3. Kotlin基础(三)类、对象和接口

    类.对象和接口 一.定义类的继承结构 一)Kotlin中的接口 Kotlin的接口与Java8中相似,它们可以包含抽象方法的定义以及非抽象方法的实现,但它们不能包含任何状态. interface Cl ...

  4. 类和对象在JVM中是如何存储的,竟然有一半人回答不上来!

    前言 这篇博客主要来说说类与对象在JVM中是如何存储的,由于JVM是个非常庞大的课题,所以我会把他分成很多章节来细细阐述,具体的数量还没有决定,当然这不重要,重点在于是否可以在文章中学到东西,是否对J ...

  5. Kotlin类:功能更强、而更简洁(KAD 03)

    作者:Antonio Leiva 时间:Dec 7, 2016 原文链接:http://antonioleiva.com/classes-kotlin/ Kotlin类尽可能简单,这样用较少的代码完成 ...

  6. Java编程里的类和对象

    像我们搞计算机这块的,都知道这么一件事,当前的计算机编程语言主要分为两大块,一为面向过程,二为面向对象.Java就是一门纯面向对象的语言.学习了一个月左右的Java,在下对于Java当中的类和对象有了 ...

  7. Python - 类与对象的方法

    类与对象的方法

  8. C++基础知识(5)---类和对象

    终于把C++中的基础在前面的几篇博客中总结完了,可能还有一些语法还没有总结到,没关系,以后用到了再查资料就好.类是C++中的一个非常重要的概念,这是区别你使用的C++到底是面向过程还是面向对象的一个重 ...

  9. 简述JavaScript对象、数组对象与类数组对象

    问题引出 在上图给出的文档中,用JavaScript获取那个a标签,要用什么办法呢?相信第一反应一定是使用document.getElementsByTagName('a')[0]来获取.同样的,在使 ...

  10. 前端学PHP之面向对象系列第一篇——类和对象

    × 目录 [1]类 [2]成员属性[3]成员方法[4]对象[5]成员访问[6]this 前面的话 面向对象程序设计(OOP)是一种计算机编程架构.计算机程序由单个能够起到子程序作用的单元或对象组成,为 ...

随机推荐

  1. 【Azure 应用服务】Azure Function App在部署时候遇见 503 ServiceUnavailable

    问题描述 在VS Code中编写好 Azure Function App代码后,通过  func azure functionapp publish 部署失败,抛出 503 Service Unava ...

  2. RPA是啥?是干嘛的?如何入门开始使用?(一)

    1.RPA是啥? 我们先对RPA有一个大概的了解,再循序渐进. Robotic Process Automation(机器人流程自动化,简称RPA). 我的简单理解就是自动化,类似于按键精灵,相对来说 ...

  3. Java 小练习(2) 利用面向对象的编程方法 设计类Circle计算圆的面积

    1 package com.bytezero.exer; 2 /*** 3 * 4 * @Description 5 * @author Bytezero·zhenglei! Email:420498 ...

  4. C++ Qt开发:QFileSystemModel文件管理组件

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用QFi ...

  5. ymal & properties 赋值特性 JSR303数据校验

    基本语法 1.空格不能省略 2.以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的. 3.属性和值的大小写都是十分敏感的. key:空格value 字面量直接写在后面就可以 , 字符串默认 ...

  6. C语言中的强制转换

    许久没有遇到的问题   C语言真是博大精深,越使用它,就越发感觉到它的威力和恐怖,最近在做算法的时候,遇到了一个强转的错误,把人折腾的够受,这次要好好梳理一下了,希望下次不能再犯此类的问题. 强制转换 ...

  7. Navicat 15下载教程

    Navicat 15下载_永久激活注册码(附图文安装教程) 欢迎关注博主公众号「java大师」, 专注于分享Java领域干货文章, 关注回复「资源」, 免费领取全网最热的Java架构师学习PDF, 转 ...

  8. 【开源库推荐】#5 Android高亮引导库

    原文:[开源库推荐]#5 Android高亮引导库 - Stars-One的杂货小窝 本文介绍2个高亮引导库HighLightPro和Curtain hyy920109/HighLightPro: A ...

  9. 使用Servlet实现单文件上传

    一位朋友最近在学习JavaWeb开发,开始学习单文件上传操作,他自己尝试着去网上看一些博客教程,能明白其中大概的思路, 还是让我和他说说,如何实现单文单件上传功能.我和他说了一下大致的思路与操作步骤, ...

  10. python高级技术(网络编程二)

    一 粘包现象(基于TCP协议实现远程执行命令) 1.TCP协议,会出现粘包现象 例:ipconfig命令,客户端收到的字符串比较短,客户端能够收完整, tasklist命令,客户端收到的字符串超过10 ...