1.5 类和对象
Kotlin语言是支持面向对象编程的,即要先将事物封装成具体的类,然后将事物所拥有的属性以及能力定义为类的字段和函数,然后再适当时候创建类的对象,调用对象中的字段和函数来满足实际编程的需求,这是面向对象编程的基本思想。
创建一个Person类:
class Person {
var name = ""
var age = 0
fun eat() {
println("$name is eating.He is $age years old.")
}
}
使用var关键字创建,因为需要在创建对象之后在指定具体的名字和年龄,需要它是一个可变值。代码中 $ 是一个转义符。
对类进行实例化,即创建一个类的对象,代码如下:
fun main() {
val p1 = Person()
p1.name = "Rose"
p1.age = 21
p1.eat()
}
直接使用Pearson() 来创建对象。
1.6 继承与构造函数
创建一个Student类,继承于Person,因为Student同样具有名字和年龄,不需要在重新赋予这些属性,会显得冗余,因此可以直接继承Student。
在Kotlin中,任何一个非抽象类都是不可继承的,相当于java中的对类赋予final声明,禁止继承。这样做的原因同val关键字,如果一个类不是专门为了继承而实现的,就应该赋予final关键字,因此Kotlin默认所有非抽象类是不可继承的。
tips:强调是非抽象类的原因是,抽象类不能创建实例,一定要由子类继承它才能创建实例,因此抽象类必须可以被继承,要不然就没有意义了。
要想使一个类被继承需要做两件事:
第一件事:需要在类前加 open 关键字。告诉编译器,这是专门为继承而创建的类,这样Person就可以被继承了。
open class Person {
...
}
第二件事:在创建继承类时加冒号 : 继承父类(),代码如下:
class Student : Person() {
var sno = ""
var grade = 0
}
父类后必须要有 () ,因为Kotlin涉及到主构造函数、次构造函数等方面的知识。
1.7 接口
接口是用于实现多态编程的重要组成部分。Kotlin是单继承结构的语言,只能继承一个父类,但可以实现多个接口。
我们可以在接口中定义一系列抽象行为,然后用具体的类去实现。使用interface关键字定义接口。
可以对接口中的函数进行默认实现!
interface Study {
fun readBooks()
fun doHomeWork(){
println("do homework default implementation!")
}
}
Kotlin实现接口统一使用 冒号: 来实现,不同于java中的implements,实现接口代码如下:
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!")
}
}
必须要实现接口中的所有函数!重写override。当接口中函数有默认实现时,这个函数可以自由选择实现或者不实现,不实现即为默认实现,可以删掉Student类中的override fun doHomeWork() 函数体。
可见性修饰符:
Kotlin中有四种:private 、protected 、public 和 internal, 需要哪种修饰符时,直接写在fun前即可。与java修饰符的区别如下:
Kotlin默认是public 所有类可见,java默认是default,同一包路径下的类可见。
1.8 数据类和单例类
数据类,用于将服务器端或数据库中的数据映射到内存中,为编程逻辑提供数据模型的支持。MVC、MVP、MVVM等架构模式,M指的是数据类。不同于java,Kotlin提供了data关键字,可自动实现数据类功能。
data class Cellphone(val brand: String, val price: Double)
class前加data自动重写了equals()、hashCode()、toString()等函数方法。
单例类,它可以用于避免创建重复的对象。比如我们希望某个类在全局仅拥有一个实例,这时可以用单例模式,创建一个单例类。同样不同于java创建单例类的方法,在Kotlin中仅需将class转化为Object即可。
object Singleton {
fun singletonTest() {
println("singleton is called.")
}
}
不同于java,不需要在类中定义getSingleton()的私有方法,也不需要私有定义构造函数等,只需要Object关键字即可。
调用方式,类似于java调用方式:
fun main() {
Singleton.singletonTest()
}
看上去像静态方法的调用,但是Kotlin自动帮我们创建了一个Singleton的实例,并保证只有一个。
1.9 Lambda编程
1.9.1 集合的创建与遍历
集合主要有List、Set、Map等。
不同于java,Kotlin提供了更简单的创建集合的方式,使用内置的listOf() 函数来创建集合。不需要再像java一点点像list中add元素,可直接一步创建。
fun main() {
val list = listOf("Apple","orange","StrawBerry")
for (fruit in list) {
println(fruit)
}
}
但是,使用listOf创建的集合是不可变集合,即不可以对集合中的元素进行增删改,同样是为了保证不可变性。
创建可变集合有另一种创建函数,mutableListOf()来创建,创建出来的集合自带增删改等函数进行操作。
fun main() {
val list2 = mutableListOf("Apple","orange","StrawBerry")
list2.add("watermelon")
list2.remove("orange")
for (fruit in list2) {
println(fruit)
}
}
同理,Set集合的用法基本一致,函数变为 setOf() 和 mutableSetOf()。
注意Set集合中不可以存放重复元素,如果存放重复元素只会保留一份。
fun main(){
val set = mutableSetOf(1, 2, 3, 4, 5, 5, 4)
println(set)
}
打印结果为: 去掉了重复元素。
Map集合,是一种键值对形式的数据结构 ,构建Map集合,以及向Map添加一条数据代码如下:
val map = HashMap<String, Int>()
map["java"] = 89
map["Kotlin"] = 92
这不是最简单的写法,因为Kotlin提供了mapOf() 和 mutableMapOf() 来创建集合,可直接传入初始化键值对来完成对Map集合的创建。
val map1 = mutableMapOf("java" to 80, "Kotlin" to 92, "C++" to 94)
这里的to并不是一个关键字,而是一个函数,现在还不会!
遍历map:
fun main(){
for ((language, score) in map1) {
println("$language 's score: $score")
}
}
1.9.2 集合的函数式API
Lambda表达式的语法结构:
{ 参数名1 : 参数类型 , 参数名2 : 参数类型 -> 函数体 }
-> 后表示参数列表的结束以及函数体的开始,函数体中可以编写任意行代码,并且最后一行代码会自动作为Lambda表达式的返回值。
首先定义一个Lambda表达式,然后lambda作为maxByOrNull的参数。
val list = mutableListOf("Apple","orange","StrawBerry")
val lambda = { fruit:String -> fruit.length }
val maxLengthFruit = list.maxByOrNull{ fruit: String -> fruit.length }
不能理解如何工作,可以看maxByOrNull的源码:
public inline fun <T, R : Comparable<R>> Iterable<T>.maxByOrNull(selector: (T) -> R): T? {
val iterator = iterator()
if (!iterator.hasNext()) return null
var maxElem = iterator.next()
if (!iterator.hasNext()) return maxElem
var maxValue = selector(maxElem)
do {
val e = iterator.next()
val v = selector(e)
if (maxValue < v) {
maxElem = e
maxValue = v
}
} while (iterator.hasNext())
return maxElem
}
其中 selector: (T) -> R 为Lambda表达式,此时我们输入的表达式为lambda,然后根据lambda来判断出最长的元素,直到遍历完整个list。
进一步简化代码,我们不需要额外定义一个lambda,可直接传递表达式作为参数。
val maxLengthFruit = list.maxByOrNull({ fruit: String -> fruit.length })
Kotlin有规定,当Lambda参数作为函数的最后一个参数时,可以将Lambda表达式移到函数括号的外面,如下:
val maxLengthFruit = list.maxByOrNull() { fruit: String -> fruit.length }
如果Lambda参数是函数的唯一一个参数的话,还可以将函数的括号省略:
val maxLengthFruit = list.maxByOrNull { fruit: String -> fruit.length }
Kotlin有出色的类型推导机制,一般不需要声明参数类型,因此可简化为:
val maxLengthFruit = list.maxByOrNull { fruit -> fruit.length }
最后,当Lambda表达式的参数列表中只有一个参数时,也可以不必声明参数名,直接使用it关键字代替:
val maxLengthFruit = list.maxByOrNull { it.length }
集合中的map函数是一种最常用的函数式API,可以将集合中的每个元素映射成另外的值,映射规则在Lambda表达式中指定。
list.map { it.uppercase() }
map的源码为:
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}