Kotlin 面向对象编程
面向对象编程
类与对象
Kotlin中也是使用class关键字来声明一个类的
Kotlin中实例化一个类的方式和Java是基本类似的,只是去掉了new关键字而已
class Person { var name = "" var age = 0 fun eat() { println(name + " is eating. He is " + age + " years old.") } } fun main() { val p = Person() p.name = "Jack" p.age = 19 p.eat() }
继承
在Kotlin中任何一个非抽象类默认都是不可以被继承的,相当于Java中给类声明了final关键字
抽象类本身是无法创建实例的,一定要由子类去继承它才能创建实例,因此抽象类必须可以被继承才行,要不然就没有意义了。Kotlin中的抽象类和Java中并无区别
// open关键字表示允许被继承 open class Person { var name = "" var age = 0 fun eat() { println(name + " is eating. He is " + age + " years old.") } } // 在Java中继承的关键字是extends,而在Kotlin中变成了一个冒号 class Student : Person() { var sno = "" var grade = 0 } fun main() { val s = Student() s.eat() }
主构造函数
主构造函数是最常用的构造函数,每个类默认都会有一个不带参数的主构造函数,当然也可以显式地给它指明参数。
主构造函数的特点是没有函数体,直接定义在类名的后面即可。比如下面这种写法:
//由于构造函数中的参数是在创建实例的时候传入的,不像之前的写法那样还得重新赋值,因此可以将参数全部声明成val。 class Student(val sno: String, val grade: Int) : Person() { } fun main() { // 在对Student类进行实例化的时候,必须传入构造函数中要求的所有参数 val student = Student("a123", 5) student.eat() }
主构造函数没有函数体,如果想在主构造函数中编写一些逻辑,该怎么办呢? Kotlin给我们提供了一个init结构体,所有主构造函数中的逻辑都可以写在里面:
class Student(val sno: String, val grade: Int) : Person() { init { println("sno is " + sno) println("grade is " + grade) } }
将Person改造一下,将姓名和年龄都放到主构造函数当中
open class Person(val name: String, val age: Int) { fun eat() { println(name + " is eating. He is " + age + " years old.") } } class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age) { } fun main() { val student = Student("a123", 5, "Jack", 19) student.eat() }
次构造函数
其实几乎是用不到次构造函数的,Kotlin提供了一个给函数设定参数默认值的功能,基本上可以替代次构造函数的作用
任何一个类只能有一个主构造函数,但是可以有多个次构造函数。次构造函数也可以用于实例化一个类,这一点和主构造函数没有什么不同,只不过它是有函数体的。
Kotlin规定,当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用)。
//次构造函数是通过constructor关键字来定义的 class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age) { //通过this关键字调用了主构造函数 constructor(name: String, age: Int) : this("", 0, name, age) { } //通过this关键字调用了上面的次构造函数 constructor() : this("", 0) { } } fun main() { // 那么现在我们就拥有了3种方式来对Student类进行实体化, // 分别是通过不带参数的构造函数、通过带两个参数的构造函数和通过带4个参数的构造函数,对应代码如下所示: val student1 = Student() val student2 = Student("Jack", 19) val student3 = Student("a123", 5, "Jack", 19) student1.eat() }
来看一种非常特殊的情况:类中只有次构造函数,没有主构造函数。这种情况真的十分少见,但在Kotlin中是允许的。当一个类没有显式地定义主构造函数且定义了次构造函数时,它就是没有主构造函数的。结合代码来看一下:
//Student类的后面没有显式地定义主构造函数,同时又因为定义了次构造函数,所以现在Student类是没有主构造函数的。 // 那么既然没有主构造函数,继承Person类的时候也就不需要再加上括号了。 class Student : Person { // 由于没有主构造函数,次构造函数只能直接调用父类的构造函数,上述代码也是将this关键字换成了super关键字 constructor(name: String, age: Int) : super(name, age) { } }
接口
Kotlin中的接口部分和Java几乎是完全一致的
interface Study { fun readBooks() fun doHomework() } //Java中继承使用的关键字是extends,实现接口使用的关键字是implements,而Kotlin中统一使用冒号,中间用逗号进行分隔 // 接口的后面不用加上括号,因为它没有构造函数可以去调用。 class Student(name: String, age: Int) : Person(name, age), Study { // Kotlin中使用override关键字来重写父类或者实现接口中的函数 override fun readBooks() { println(name + " is reading.") } override fun doHomework() { println(name + " is doing homework.") } } /* 这里为了演示一下多态编程的特性,故意将代码写得复杂了一点。 首先创建了一个Student类的实例,本来是可以直接调用该实例的readBooks()和doHomework()函数的, 但是没有这么做,而是将它传入到了doStudy()函数中。
doStudy()函数接收一个Study类型的参数,由于Student类实现了Study接口, 因此Student类的实例是可以传递给doStudy()函数的, 接下来我们调用了Study接口的readBooks()和doHomework()函数, 这种就叫作面向接口编程,也可以称为多态。 */ fun main() { val student = Student("Jack", 19) doStudy(student) } fun doStudy(study: Study) { study.readBooks() study.doHomework() }
Kotlin还增加了一个额外的功能:允许对接口中定义的函数进行默认实现。其实Java在JDK 1.8之后也开始支持这个功能了,因此总体来说,Kotlin和Java在接口方面的功能仍然是一模一样的。
interface Study { fun readBooks() fun doHomework() { println("do homework default implementation.") } } 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.") // } }
函数的可见性修饰符
和Java相比变化比较大的部分——函数的可见性修饰符
Java和Kotlin函数可见性修饰符对照表
数据类
在一个规范的系统架构中,数据类通常占据着非常重要的角色,它们用于将服务器端或数据库中的数据映射到内存中,为编程逻辑提供数据模型的支持。或许你听说过MVC、MVP、MVVM之类的架构模式,不管是哪一种架构模式,其中的M指的就是数据类。
数据类通常需要重写equals()、hashCode()、toString()这几个方法。
在一个类前面声明了data关键字时,就表明你希望这个类是一个数据类,Kotlin会根据主构造函数中的参数帮你将equals()、hashCode()、toString()等固定且无实际逻辑意义的方法自动生成
当一个类中没有任何代码时,还可以将尾部的大括号省略
data class Cellphone(val brand: String, val price: Double) fun main() { val cellphone1 = Cellphone("Samsung", 1299.99) val cellphone2 = Cellphone("Samsung", 1299.99) println(cellphone1) println("cellphone1 equals cellphone2 " + (cellphone1 == cellphone2)) }
单例类
在Kotlin中创建一个单例类的方式极其简单,只需要将class关键字改成object关键字即可。
object Singleton { fun singletonTest() { println("singletonTest is called.") } } fun main() { // 这种写法虽然看上去像是静态方法的调用,但其实Kotlin在背后自动帮我们创建了一个Singleton类的实例,并且保证全局只会存在一个Singleton实例。 Singleton.singletonTest() }