【Kotlin】学习笔记(二)面向对象编程

一、类与对象

下面的将示范如何创建一个Kotlin类:

1
2
3
4
5
6
7
8
class Person {      //类名:Person
var name = "" //类成员变量
var age = 0

fun show() { //类成员函数
println(name + "," + age + "岁")
}
}

然后将这个类进行实例化,获得一个对象

1
2
3
4
5
6
fun main() {
val p = Person() //跟Java相比,去掉了new关键字
p.name = "田所浩二"
p.age = 24
p.show()
}

二、类的继承与构造函数

首先,在Kotlin中,所有非抽象类默认都是不可继承的,就像Java中的final声明的类,要使得类能悲继承,需要open关键字。

1
2
3
open class Person {      //类名:Person
...
}

然后我们让Student类继承Person类,使用一个冒号,注意被继承类名后面的括号:

1
2
3
4
class Student : Person() {
var id = ""
var grade = 0
}

另外是关于构造函数的部分。Kotlin将构造函数分成了两种:主构造函数次构造函数

1.主构造函数

主构造函数是每个类默认都会有的一个不带参数的构造函数,我们也可以给它指定参数。主构造函数没有函数体,直接定义在类名后面:

1
2
3
4
5
6
class Student(val id: String, val grade: Int): Person() {
//在主构造函数中声明为val或var的参数会自动成为该类的成员
}

//实例化的时候:传入构造函数的所有参数:
val student = Student("1145141919810", 5)

主构造函数没有函数体,如果想在其中编写逻辑,可以使用init结构体:

1
2
3
4
5
6
class Student(val id: String, val grade: Int): Person() {
init {
println("id is " + id)
println("grade is " + grade)
}
}

根据继承的规定,子类的构造函数必须调用父类的构造函数,我们就需要在括号中填入父类构造函数的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
//将Person类的主构造函数改为:
open class Person(val name: String, val age: Int) {
...
}

//将Student类的继承改为:
class Student(val id: String, val grade: Int, name: String, age: Int):
Person(name, age) { //这里的name, age仅作为参数,不生成成员字段,防止父类同名成员的冲突
init {
println("id is " + id)
println("grade is " + grade)
}
}

2.次构造函数

一个类能有多个次构造函数,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Student(val id: String, val grade: Int, name: String, age: Int): 
Person(name, age)
{

constructor(name: String, age: Int): this("", 0, name, age) {

}

constructor(): this("", 0) {

}
}

val st1 = Student()
val st2 = Student("我修院卓", 23)
val st3 = Student("1145141919810", 5, "田所浩二", 24)

如果类中没有主构造函数,只有次构造函数该怎么继承?代码如下:

1
2
3
4
class Student : Person { //没有主构造,故不用写括号
//以super关键字直接调用父类构造函数
constructor(name: String, age: Int) : super(name, age) {}
}

三、接口

不同于C++的多继承,Java和Kotlin都是单继承结构的语言,最多只允许继承一个父类,但可以实现任意个接口 接口中定义的函数可以不实现,也可以定义默认的实现。Kotlin只会强制要求实现没有默认实现的函数。 接口定义方法如下:

1
2
3
4
5
6
interface Study {
fun readBooks()
fun doHomework() {
println("doing homework") //默认的实现
}
}

Student类实现这个接口, 和继承的写法一样,然后再使用override关键字重载接口定义的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Student(name: String, age: Int) : Person(name, age), Study { 
override fun readBooks() {
println(name + " is reading.")
}
override fun doHomework() {
println(name + " is doing homework")
}
}

//使用这个类:
fun main() {
val stu = Student("田所浩二", 24)
doStudy(stu)
}

fun doStudy(stu: Study) {
//只有继承了Study接口的类的对象才能使用该函数
stu.readBooks()
stu.doHomework()
}

四、可见性修饰符

Java中有四种修饰符:publicprivateprotecteddefault,默认是default

Kotlin也有四种:publicprivateprotectedinternal, 默认是public

修饰符 Java Kotlin
public 所有类可见 所有类可见(默认)
private 当前类可见 当前类可见
protected 当前类、子类、同一煲路径下的类可见 当前类,子类可见
default 同一包路径下的类可见(默认)
internal 同一模块中的类可见

五、数据类

在一个规范的系统架构中,数据类用于将服务器端或数据库中的数据映射到内存中,为编程逻辑提供数据模型的支持。

在Java中数据类通常需要重写equals()hashCode()toString()这几个方法,而Kotlin则只需要一个data关键字,这些方法便会自动生成:

1
data class Cellphone(val brand: String, val price: Double)

当一个类内部没有任何代码,便可以将大括号省略。 下面测试一下这个数据类:

1
2
3
4
5
6
fun main() {
val cellphone1 = Cellphone("Samsung", 1299.99)
val cellphone2 = Cellphone("Samsung", 1299.99)
println(cellphone1)
println("cellphone1 == cellphone2 ? " + (cellphone1 == cellphone2))
}

运行结果如下:

Cellphone(brand=Samsung, price=1299.99)
cellphone1 == cellphone2 ? true

六、单例类

使用单例可以避免创建重复的对象,保证一个类最多拥有一个实例。

Java中创建单例类需要以下几个步骤:

  1. 将类的构造函数私有化
  2. 向外部提供一个getInstance()静态方法用于获取该类实例
  3. getInstance()中,判断如果当前缓存的实例为null,就创建一个新的实例并将其缓存,否则直接返回缓存的实例即可。

Kotlin中,只需要将class关键字改成object关键字即可声明单例类:

1
2
3
4
5
6
7
8
object Singleton {
fun test() {
println("test is called")
}
}

//调用方式
Singleton.test()

看上去就像是调用了静态方法,其实是Kotlin在背后自动创建了一个类的实例,并且保证全局只有一个该类的实例。


【Kotlin】学习笔记(二)面向对象编程
https://chordfish-k.github.io/2023/02/02/kotlin/kotlin-20230202/
作者
超弦鱼
发布于
2023年2月2日
许可协议