Kotlin 面向对象编程
面向对象编程
类与对象
Kotlin中也是使用class关键字来声明一个类的
Kotlin中实例化一个类的方式和Java是基本类似的,只是去掉了new关键字而已
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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中并无区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // 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() } |
主构造函数
主构造函数是最常用的构造函数,每个类默认都会有一个不带参数的主构造函数,当然也可以显式地给它指明参数。
主构造函数的特点是没有函数体,直接定义在类名的后面即可。比如下面这种写法:
1 2 3 4 5 6 7 8 9 | //由于构造函数中的参数是在创建实例的时候传入的,不像之前的写法那样还得重新赋值,因此可以将参数全部声明成val。 class Student(val sno: String, val grade: Int) : Person() { } fun main() { // 在对Student类进行实例化的时候,必须传入构造函数中要求的所有参数 val student = Student( "a123" , 5 ) student.eat() } |
主构造函数没有函数体,如果想在主构造函数中编写一些逻辑,该怎么办呢? Kotlin给我们提供了一个init结构体,所有主构造函数中的逻辑都可以写在里面:
1 2 3 4 5 6 | class Student(val sno: String, val grade: Int) : Person() { init { println( "sno is " + sno) println( "grade is " + grade) } } |
将Person改造一下,将姓名和年龄都放到主构造函数当中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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规定,当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //次构造函数是通过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中是允许的。当一个类没有显式地定义主构造函数且定义了次构造函数时,它就是没有主构造函数的。结合代码来看一下:
1 2 3 4 5 6 7 | //Student类的后面没有显式地定义主构造函数,同时又因为定义了次构造函数,所以现在Student类是没有主构造函数的。 // 那么既然没有主构造函数,继承Person类的时候也就不需要再加上括号了。 class Student : Person { // 由于没有主构造函数,次构造函数只能直接调用父类的构造函数,上述代码也是将this关键字换成了super关键字 constructor(name: String, age: Int) : super (name, age) { } } |
接口
Kotlin中的接口部分和Java几乎是完全一致的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 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()函数中。<br>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在接口方面的功能仍然是一模一样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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()等固定且无实际逻辑意义的方法自动生成
当一个类中没有任何代码时,还可以将尾部的大括号省略
1 2 3 4 5 6 7 8 | 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关键字即可。
1 2 3 4 5 6 7 8 9 10 | object Singleton { fun singletonTest() { println( "singletonTest is called." ) } } fun main() { // 这种写法虽然看上去像是静态方法的调用,但其实Kotlin在背后自动帮我们创建了一个Singleton类的实例,并且保证全局只会存在一个Singleton实例。 Singleton.singletonTest() } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2021-01-13 MySQL MyISAM表锁
2021-01-13 MySQL锁概述
2019-01-13 springboot集成mybatis
2019-01-13 idea搭建springboot
2017-01-13 mui 对话框 点击按钮不关闭对话框的办法
2017-01-13 CSS 背景(css background)