学习Kotlin之面向对象编程基础(一)
继承与构造函数
Kotlin中任何一个非抽象类默认都是不可以被继承的,相当于Java中给类声明了final关键字。抽象类本身是无法创建实例的,一定要由子类去继承它才能创建实例。在类前加上open关键字就可以被继承了。
继承的关键字是冒号 ‘:’。
为什么Person类要加括号呢?
Kotlin中将构造函数分为了两种:主构造函数和次构造函数。
主构造函数是最常用的,每个类默认都会有一个不带参数的主构造函数,也可以显示指明参数。主构造函数的特点是没有函数体,直接定义在类名后面。
如果要在主构造函数中编写逻辑,用到init结构。
Java继承特性中有一个规定:子类中的构造函数必须调用父类中的构造函数。Kotlin也要遵守。所以采用了括号,表示student类的主构造函数在初始化的时候会调用person类的无参构造函数,即使在无参数的情况下,括号也不能省略。
如果将person改成姓名和年龄都放在主构造函数中:
此时student类会报错,因为空括号表示调用person类的无参构造函数,但是现在person类已经没有无参构造函数了。
解决方法:必须给person类的构造函数加上name和age字段。再将这两个参数传给person类的构造函数。
注意:此时不能将name和age字段声明成val,因为在主构造函数中声明了就会自动成为该类的字段,会导致和父类中同名的name和age造成冲突。因此不加任何关键字,让它的作用域仅限定于主构造函数中。
虽然有次构造函数,但是几乎用不到。关键字是constructor。当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数,使用this关键字。
特殊情况:类中只有次构造函数,没有主构造函数。
这个时候没有主构造函数,继承person类的时候就不需要加上括号了。由于没有主构造函数,次构造函数只能调用父类的构造函数,使用super关键字。
接口
接口是实现多态编程的重要组成部分。我们在接口中定义一系列的抽象行为,然后由具体的类去实现。
定义接口:
实现接口:
接口不用加括号,因为它没有构造函数。接口中定义了两个待实现函数,因此student类必须实现这两个函数。
多态编程:
首先创建了一个Student类的实例,本来是可以直接调用该实例的readBooks()和doHomework()函数的,但是没有这么做,而是将它传入到了doStudy()函数中。doStudy()函数接收一个Study类型的参数,由于Student类实现了Study接口,因此Student类的实例是可以传递给doStudy()函数的,接下来我们调用了Study接口的readBooks()和doHomework()函数,这种就叫作面向接口编程,也可以称为多态。
可以对接口中定义的函数进行默认实现(Java在jdk1.8之后也支持):
函数的可见性修饰符
数据类与单例类
MVC、MVP、MVVM架构中的M都是数据类,作用是将服务器端或数据库中的数据映射到内存中,为编程逻辑提供数据模型的支持。
数据类通常需要重写equals(),hashCode(),tostring()方法。
在Java中实现数据类:
然而这些代码没有实际意义,只是让它拥有了数据类的功能。
在kotlin中,只需要在类前声明一个data关键字即可。equals()、hashCode()、toString()会自动生成。
Kotlin特有的功能——单例类
我们希望某个类在全局最多只能拥有一个实例,就使用单例模式,可以避免创建重复对象。
Java中最常见的写法:
理解:首先为了禁止外部创建Singleton的实例,将其构造函数私有化,然后给外部提供一个getInstance()静态方法用于获取Singleton的实例。
使用方法:
在Kotlin中,将class关键字改成object关键字即可。
现在Singleton是一个单例类了。可以直接在里面编写函数。
Lambda编程
集合的创建与遍历
集合:List,Set,Map。
List的主要实现类是ArrayList,LinkedList;
Set的主要实现类是HashSet;
Map是HashMap。
Kotlin提供了一个内置的listOf()函数来简化初始化集合的写法:
For-in循环不仅可以遍历区间,还能遍历集合。
但是,listOf()创建的是一个不可变的集合。即该集合只能用于读取,不能对集合进行添加、修改和删除。
而创建可变集合就使用mutableListOf()函数。
Set集合的用法也是一样的,只是函数名换成了setOf()和mutableSetOf()。
注意:Set集合是不能存放重复元素的。
Map集合,传统的用法是先创建一个HashMap的实例,然后将一个个键值对添加进去。
但在kotlin中不推荐使用put和get。而是使用类似数组下标。
添加元素:
读取:
Kotlin提供了mapOf()和mutableMapOf():
其中的to并不是关键字,而是一个infix函数。
集合的函数式API
从这里开始了解Lambda。
Lmabda就是一小段可以作为参数传递的代码。正常情况下,我们想某个函数传参时只能传入变量,而借助Lambda可以传入一小段代码。
语法结构:
->表示参数列表的结束以及函数体的开始。
函数体中可以编写任意行代码(不建议太长),并且最后一行代码会自动作为Lambda表达式的返回值。
首先我们来思考一个需求,如何在一个水果集合里面找到单词最长的那个水果?
普通代码如下:
最终简化版本:
其实maxBy是一个普通的函数,它接收一个Lambda类型的参数,并且会在遍历集合时将每次遍历的值作为参数传递给Lambda表达式。
简化过程:
首先,套用Lambda表达式的语法结构,并将它传入到maxBy函数中:
其次不用专门定义lambda变量,可以直接将lambda表达式传入maxBy函数中:
Kotlin规定,当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到函数括号外面:
接下来,如果Lambda参数是函数的唯一一个参数的话,可以将括号省略:
由于Kotlin有类型推导机制,Lmabda表达式中的参数列表在大多数情况下不必声明参数类型:
最后,当Lambda表达式的参数列表只有一个参数时,也不必声明参数名,而是使用it关键字代替:
集合中的map函数是最常用的一种函数式API,它用于将集合中的每个元素都映射成一个另外的值,映射规则在Lambda表达式中指定,最终生成一个新的集合。
比如:我们让所有的水果名都变成大写模式:
接下来学习一个比较常用的函数式API——filter函数,any和all函数。
用来过滤集合中的数据的。可以单独使用,也可以配合map函数。
比如我们只想保留5个字母以内的水果,借助filter函数:
Any函数用于判断集合中是否至少存在一个元素满足指定条件。
All函数用判断集合中是否所有元素都满足指定条件。
这两个返回结果为Boolean类型。
Java函数式API的使用
在kotlin中调用Java方法时也可以使用函数式API,只不过有一定条件限制。
具体的,在kotlin代码中调用一个Java方法,并且该方法接收一个Java单抽象方法接口参数,就可以使用函数式API。Java单抽象方法接口指的是接口中只有一个待实现的方法,如果接口中有多个待实现方法,则无法使用函数式API。
例子:
Java原生API中有一个最为常见的单抽象方法接口——Runnable接口。里面只有一个待实现的run()方法:
所以对于任何一个Java方法,只要它接受Runnable参数,就可以使用函数式API。
Runnable接口主要还是结合线程一起使用,Thread类的构造方法中接收了一个Runnable参数,我们可以使用Java代码创建并执行一个子线程:
这里使用的匿名类的写法。创建了一个Runnable接口的匿名类实例,并将它传给了Thread类的构造方法,最后调用Thread类的start()方法执行这个线程。
如果使用kotlin写法:
由于kotlin完全舍弃了new关键字,因此创建匿名类实例的时候就不能再使用new了,而是改用了object关键字。
因为Runnable类中只有一个待实现方法,即使没有显式重写run()方法,kotlin也能知道Runnable后面的Lambda表达式就是要在run()中实现的内容。
其次,如果一个Java方法的参数列表有且仅有一个Java单抽象方法接口参数,就可以将接口名省略:
不过,当Lambda表达式是方法的最后一个参数时,可以将表达式移到括号外面,同时,如果Lambda表达式还是方法的唯一一个参数,就可以将方法的括号省略。
最终简化结果:
我们经常打交道的Android SDK还是使用Java语言编写的,但我们在kotlin中调用这些SDK接口时,就很可能会用到这种Java函数式API的写法。
比如,Android中极为常用的点击事件接口OnClickListener,其定义如下:
使用Java代码去注册按钮的点击事件:
而用kotlin实现,就可以使用函数式API的写法:
最后,本次学习都Java函数式API的使用都限定于从Kotlin中调用Java放大,并且单抽象方法接口也必须是用Java语言定义的。因为kotlin中有专门的高阶函数来实现更加强大的自定义函数式API功能,从而不需要像Java这样借助单抽象方法接口来实现。
以上知识来源自《第一行代码(第3版)》,记录下来方便学习。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)