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