目录
2.5 面向对象编程
和很多现代高级语言一样,Kotlin 也是面向对象的,因此理解什么是面向对象编程对我们来说就非常重要了。关于面向对象编程的解释,你可以去看很多标准化、概念化的定义,但是我觉得 那些定义只有本来就懂的人才能看得懂,而不了解面向对象的人,即使看了那些定义还是不明白什么才是面向对象编程。
因此,这里我想用自己的理解来向你解释什么是面向对象编程。不同于面向过程的语言(比如 C 语言),面向对象的语言是可以创建类的。类就是对事物的一种封装,比如说人、汽车、房 屋、书等任何事物,我们都可以将它封装一个类,类名通常是名词。而类中又可以拥有自己的字段和函数,字段表示该类所拥有的属性,比如说人可以有姓名和年龄,汽车可以有品牌和价格,这些就属于类中的字段,字段名通常也是名词。而函数则表示该类可以有哪些行为,比如说人可以吃饭和睡觉,汽车可以驾驶和保养等,函数名通常是动词。
通过这种类的封装,我们就可以在适当的时候创建该类的对象,然后调用对象中的字段和函数来满足实际编程的需求,这就是面向对象编程最基本的思想。当然,面向对象编程还有很多其 他特性,如继承、多态等,但是这些特性都是建立在基本的思想之上的,理解了基本思想之后,其他的特性我们可以在后面慢慢学习。
2.5.1 类与对象
现在我们就按照刚才所学的基本思想来尝试进行面向对象编程。首先创建一个 Person 类。右击 com.example.helloworld 包 →New→Kotlin File/Class,在弹出的对话框中输入“Person” 。 对话框在默认情况下自动选中的是创建一个 File,File通常是用于编写 Kotlin 顶层函数和扩展函数的,我们可以点击展开下拉列表进行切换,如 图2.17 所示。
![](https://img-blog.csdnimg.cn/direct/3ef1eeb0d5b7439aab50dfcf4a4a7a89.png)
这里选中 Class 表示创建一个类,按下回车键完成创建,会生成如下所示的代码:
class Person {
}
这是一个空的类实现,可以看到,Kotlin中也是使用 class 关键字来声明一个类的,这一点和 Java 一致。现在我们可以在这个类中加入字段和函数来丰富它的功能,这里我准备加入 name 和age 字段,以及一个 eat() 函数,因为任何一个人都有名字和年龄,也都需要吃饭。
class Person {
var name = ""
var age = ""
fun eat(){
println(name + " is eating. He is " + age + " years old.")
}
}
简单解释一下,这里使用 var 关键字创建了 name 和 age 这两个字段,这是因为我们需要在创建对象之后再指定具体的姓名和年龄,而如果使用 val 关键字的话,初始化之后就不能再重新赋值了。接下来定义了一个 eat() 函数,并在函数中打印了一句话,非常简单。
Person类已经定义好了,接下来我们看一下如何对这个类进行实例化,代码如下所示:
val p = Person()
Kotlin 中实例化一个类的方式和 Java 是基本类似的,只是去掉了 new 关键字而已。之所以这么设计,是因为当你调用了某个类的构造函数时,你的意图只可能是对这个类进行实例化,因此即使没有 new 关键字,也能清晰表达出你的意图。Kotlin 本着最简化的设计原则,将诸如 new、行尾分号这种不必要的语法结构都取消了。
上述代码将实例化后的类赋值到了 p 这个变量上面,p 就可以称为 Person 类的一个实例,也可以称为一个对象。
下面我们开始在 main() 函数中对 p 对象进行一些操作:
fun main(){
val p = Person()
p.name = "Jack"
p.age = "19"
p.eat()
}
这里将 p 对象的姓名赋值为 Jack,年龄赋值为 19 ,然后调用它的 eat() 函数,运行结果如 图 2.18 所示。
![](https://img-blog.csdnimg.cn/direct/9d2fba3e31e84df6aa961570f97a3fba.png)
这就是面向对象编程最基本的用法了,简单概括一下,就是要先将事物封装成具体的类,然后将事物所拥有的属性和能力分别定义成类中的字段和函数,接下来对类进行实例化,再根据具 体的编程需求调用类中的字段和方法即可。
小贴士:
class 关键字来声明一个类。
2.5.2 继承与构造函数
现在我们开始学习面向对象编程中另一个极其重要的特性——继承。继承也是基于现实场景总结出来的一个概念,其实非常好理解。比如现在我们要定义一个 Student 类,每个学生都有自 己的学号和年级,因此我们可以在 Student 类中加入 sno 和 grade 字段。但同时学生也是人呀,学生也会有姓名和年龄,也需要吃饭,如果我们在 Student 类中重复定义 name、age字段和eat()函数的话就显得太过冗余了。这个时候就可以让 Student 类去继承 Person 类,这样 Student 就自动拥有了 Person 中的字段和函数,另外还可以定义自己独有的字段和函数。
这就是面向对象编程中继承的思想,很好理解吧?接下来我们尝试用 Kotlin 语言实现上述功能。 右击 com.example.helloworld 包 →New→Kotlin File/Class,在弹出的对话框中输入“Student” ,并选择创建一个 Class,你可以通过上下按键快速切换创建类型。
按下回车完成创建,并在 Student 类中加入学号和年级这两个字段,代码如下所示:
class Student {
var sno = ""
var grade = 0
}
现在 Student 和 Person 这两个类之间是没有任何继承关系的,想要让 Student 类继承Person 类,我们得做两件事才行。
第一件事,使 Person 类可以被继承。可能很多人会觉得奇怪,尤其是有 Java 编程经验的人。一 个类本身不就是可以被继承的吗?为什么还要使 Person 类可以被继承呢?这就是 Kotlin 不同的地方,在 Kotlin中 任何一个非抽象类默认都是不可以被继承的,相当于 Java 中给类声明了final 关键字。之所以这么设计,其实和 val 关键字的原因是差不多的,因为类和变量一样,最好都是不可变的,而一个类允许被继承的话,它无法预知子类会如何实现,因此可能就会存在一些未 知的风险。Effective Java 这本书中明确提到,如果一个类不是专门为继承而设计的,那么就应该主动将它加上 final 声明,禁止它可以被继承。
很明显,Kotlin 在设计的时候遵循了这条编程规范,默认所有非抽象类都是不可以被继承的。之所以这里一直在说非抽象类,是因为抽象类本身是无法创建实例的,一定要由子类去继承它才 能创建实例,因此抽象类必须可以被继承才行,要不然就没有意义了。由于 Kotlin 中的抽象类和