Kotlin学习笔记

前言

Kotlin语言从问世到现在已经有很久的时间了,2017年终于Google将Kotlin列为开发Android应用的一级语言。而且Kotlin和Java具有高度可互操作性,并且相较于Java更加简单,精炼,还添加了Java在Android中尚不支持的一些必备功能。学习Kotlin与时俱进是必须的。

Kotlin语言基础

变量

  • var是可写的,在其生命周期中可以被多次赋值
  • val是只读的,仅能赋值一次
  • 变量类型在大部分情况下无需申明
  • is 运算符可以对变量的类型进行判断,并且检测之后的分支中可以直接当做该类型使用,无需显式转换
  • 三重引号内可以完整的输出原生字符串的格式
  • 流程控制语句
    • 分支语句(if,when)
      • if是一个表达式,可以将代码块的最后一行作为其返回值
      • 如果if表达式只有一个分支,或者分支的结果是Unit,它的值就是Unit
      • when表达式类似switch-case表达式,可以用任意表达式作为判断条件,也可以检测(in)或者(!in)在一个区间中
    • 循环语句(for,while)
      • for循环可以对任何提供迭代器的对象进行遍历
      • for循环通过索引遍历一个数组或者一个list,可以for(i in array.indices) print(array[i])
      • for循环可以使用库函数withIndux操作for((index,value) in array.withIndex()) println("the element at $index is $value")
    • 跳转语句(break,continue,return,throw)
      • 标签(abc@)可以定义跳转语句,直接跳转到标签位置

修饰符

  • modifiers
    • (modifier | annotations)*
  • typeModifiers
    • (suspendModifier | annotations)*
  • modifier
    • classModifier
    • accessModifier
    • varianceAnnotation
    • memberModifier
    • parameterModifier
    • typeParameterModifier
    • functionModifier
    • propertyModifier
  • classModifier 类修饰符
    • abstract 抽象类
    • final 不可被继承final类
    • enum 枚举类
    • open 可继承open类
    • annotation 注解类
    • sealed 密封类
    • data 数据类
  • memberModifier
    • override 重写函数
    • open 可被重写
    • final 不可被重写
    • abstract 抽象函数
    • lateinit 后期初始化
  • accessModifier 访问权限控制,默认是public
    • private
    • protected
    • public
    • internal
  • varianceAnnotation 泛型可变类
    • in
    • out
  • parameterModifier
    • noinline
    • crossinline
    • vararg 变长参数
  • typeParameterModifier
    • reified
  • functionModifier
    • tailrec 尾递归
    • operator
    • infix
    • inline
    • external
    • suspendModifier
  • propertyModifier
    • const
  • suspendModifier
    • suspend

关键字

this关键字

  • 在类的成员中,this指的是该类的对象
  • 在扩展函数或者带接收者的函数字面值中,this表示在点左侧传递的接收者参数
  • 在内部类中,它指的是最内层的包含它的作用域,如果我们想要引用其他作用域中的this,可以使用this@lable标签

super关键字

  • super关键字持有指向其父类的引用

操作符

  • 重载操作符

    • 重载操作符的函数需要用operator修饰符标记
    • 中缀操作符的函数使用infix修饰符标记
  • 相等与不相等操作符

    • 引用相等 === !== ( 两个引用指向同一个对象 ) 如果a和b指向同一个对象,则a===b为true
    • 结构相等 == != ( 使用equals()判断 )a==b --> a?.equals(b)?:(b===null)
  • Elvis操作符?:(二元运算符)
    • Elvis操作符特定是跟null比较,主要用来作null安全检查。
    • Kotlin没有true?1:0三元运算符,取而代之是if(true) 1 else 0

扩展函数和扩展属性

  • 扩展函数
    • fun String.notEmpty():Boolean { return !this.isEmpty()}
    • this关键字在扩展函数内部对应到接收者对象
  • 扩展属性
    • 扩展没有实际的将成员插入类中,它的行为只能由显式提供的getters/setters表示

空指针安全

  • ?.安全调用
    • 如果变量为null,则返回null。
  • !!.非空断言调用
    • 如果变量为null,则抛出kotlin.KotlinNullPointerException异常

标准库API

Java和JS共用的公共库

包名 功能说明
kotlin 核心函数和数据类型,支持所有平台
kotlin.annotation 为kotlin注解功能提供支持
kotlin.collections 集合类型,入Iterable、Collection、List、Set、Map等
kotlin.comparisons 帮助函数用于创建Comparator比较器实例
kotlin.coroutines.experimental(1.1) 支持协程,包括支持延迟序列(lazy sequence)等
kotlin.coroutines.experimental.intrinsics(1.1) 基于协程的API库的底层构建块
kotlin.experimental(1.1) 实验API,将来版本可能会改变
kotlin.io IO API用于处理文件和流
kotlin.properties 代理/委托属性的标准实现,帮助函数实现自定义代理/委托
kotlin.ranges 范围/区间,数列Progressions和相关扩展功能
kotlin.reflect Kotlin反射的运行时API
kotlin.sequences 序列类型表示延迟求值的集合、实例化序列和扩展函数
kotlin.text 处理文本和正则表达式的函数

Java平台的Kotlin库

包名 功能说明
kotlin.concurrent 并发(concurrent)编程Kotlin函数库
kotlin.jvm Java平台特有的函数和注解
kotlin.reflect.full(1.1) Kotlin反射库的扩展Extensions
kotlin.reflect.jvm Kotlin与Java反射的互操作运行时API
kotlin.streams(1.1,JRE8) 处理Java8流的Kotlin函数库
kotlin.system 与系统相关的Kotlin函数库

JavaScript平台的Kotlin库

包名 功能说明
kotlin.browser 在浏览器环境下访问顶层属性(如document、Window等)
kotlin.dom 处理浏览器DOM的Kotlin函数库
kotlin.js JavaScript平台特有的一些函数和API
org.khronos.webgl WebGL API的Kotlin包装器
org.w3c.dom DOM API的Kotlin包装器
org.w3c.dom.css DOM CSS API的Kotlin包装器
org.w3c.dom.events DOM events API的Kotlin包装器
org.w3c.dom.parsing DOM parsing API的Kotlin包装器
org.w3c.dom.svg DOM SVG API的Kotlin包装器
org.w3c.dom.url DOM URL API的Kotlin包装器
org.w3c.xhr XML HttpRequest API 的Kotlin包装器
org.w3c.files W3C file API的Kotlin包装器
org.w3c.notifications Web Notification API的Kotlin包装器
org.w3c.performance Navigation Timing API的Kotlin包装器
org.w3c.workers Web Workers API的Kotlin包装器

基本数据类型与类型系统

Kotlin是一门强类型、静态类型、支持隐士类型的显式类型语言。

  • 强类型:在运行时确保不会发生未经明确转换的类型转换。(一个语言的编译器引入越多的类型检查的限制,就可以称这个语言的类型检查越强)
  • 静态类型:静态类型检查能让很多bug在编码早期就被捕捉到,并且它也能优化运行。(类型检查可发生在编译时期(静态检查)或运行时期(动态检查))
  • 显式类型:定义变量时显式给出变量的类型(根据变量名是否需要显式给出类型的声明,分为显式类型语言和隐式类型语言)

根类型 Any

Kotlin中所有类都有一个共同的超类Any,同Java中java.lang.Object

基本类型

Kotlin中一切皆是对象,所有类型都是引用类型。

数字类型(Number)

类型 宽度(Bit)
Double 64
Float 32
Long 64
Int 32
Short 16
Byte 8
  • 这些内置的数据类型,都继承了Number和Comparable类。
  • Kotlin的数字类型与Java基本相同。但Kotlin对于数字没有隐式拓宽转换(如Java中int可以隐式转换为long),因为值范围较小类型并不是较大类型的子类型。
  • val i: Int = b.toInt()
    • toDouble() : Double
    • toFloat() : Float
    • toLong() : Long
    • toInt() : Int
    • toChar() : Char
    • toShort() : Short
    • toByte() : Byte
  • 位运算,没有特殊字符来表示,只可用中缀方式调用命名函数

位运算列表(只用于Int和Long)

  • shl(bits)————有符号左移(Java的<<)
  • shr(bits)————有符号右移(Java的>>)
  • ushr(bits)————无符号右移(Java的>>>)
  • and(bits)————位与
  • or(bits)————位或
  • xor(bits)————位异或
  • inv()————位非

布尔类型

  • !逻辑非:not()
  • &&短路逻辑与:and()
  • ||短路逻辑或:or()
  • 异或(相同false,不同true):xor

字符串类型

  • 索引运算符s[i]: 字符串元素————字符可以使用索引运算符s[i]来访问
  • for循环迭代字符串
  • 字符串类重载了+操作符,作用对象可以是任何对象,包括null
  • 截取字符串的子串(subSequence(0,1))
  • trimMargin(marginPrefix:String = “|”):去除默认为边界字符为”|”的前导空格
  • trimIndent():把字符串行左边空白对齐切割
  • 以美元符($)开头做字符串模板:val str = "$s.length is ${s.length}"

数组类型

  • 数组在Kotlin中使用Array类表示,定义了get和set函数以及size属性。
  • 可以使用arrayOf()来创建一个数组并传递元素值给Array
  • Kotlin还允许不同类型元素放到一个数组中
  • arrayOfNulls()可以用于创建一个指定大小、元素都为空的数组。这个特殊的空数组在创建时,需要指定元素的类型。arrayOfNulls<Int>(10)
  • 数组Array类还提供了一个构造函数public inline constructor(size:Int,init:(Int) -> T) 例:val square = Array(10,{i -> (i*i)})
  • []运算符代表调用成员函数get()和set()
  • 原生数组类型
    • BooleanArray ———— booleanArrayOf(vararg elements: Boolean)
    • ByteArray ———— byteArrayOf(vararg elements: Byte)
    • CharArray ———— charArrayOf(vararg elements: Char)
    • ShortArray ———— shortArrayOf(vararg elements: Short)
    • IntArray ———— intArrayOf(vararg elements: Int)
    • LongArray ———— longArrayOf(vararg elements: Long)
    • FloatArray ———— floatArrayOf(vararg elements: Float)
    • DoubleArray ———— doubleArrayOf(vararg elements: Double)

可空类型

  • null 是 Nothing? 类型的
  • Any?是Kotlin的类型层次结构的最顶端

Kotlin.Unit类型

Kotlin中的Unit类型实现了与Java中的void一样的功能。当一个函数没有返回值时,我们用Unit来实现这个特征,而不是null

kotlin.Nothing类型

在Kotlin类型层次结构的最底层就是类型Nothing

类型检测与类型转换

is与!is运算符

  • is运算符类似于Java的instanceOf,用来检查对象是否属于某数据类型
  • is会自动对对象的数据类型进行安全的转换

as运算符

  • as运算符用于执行引用类型的显式类型转换。如果兼容则转换成功,否则as?运算符返回null,as抛出异常

集合类

世间本无集合;
有人想要,于是就用数组创造了集合类;
有人想要可以自动扩展容量的数组,于是有了List;
有人想要元素不重复的数组,于是有了Set;
有人想要有序的数组,于是有了TreeSet,TreeList;
有人想要通过复杂对象来查找另一个对象的关联数组,于是有了Map;

  • 几乎所有集合都是基于数组来实现的,所以数组比集合要快
  • 集合是数组的高层次的抽象封装,集合的功能比数组要多
  • Kotlin集合类分为:可变集合类(Mutable)和不可变集合类(Immutable)
  • 集合类型主要有3种:list(列表)、set(集)、map(映射)

List

List接口继承与Collectio接口,元素以线性方式存储,集合中可以存放重复对象。

  • 不可变集合类List(ReadOnly,Immutable):
    • 使用listOf()函数来创建没有元素的空List
    • 使用listOf(element: T)函数来创建只有一个元素的List
    • 使用listOf(vararg elements: T)函数来创建有多个元素的List
    • asList()是Array的扩展函数
    • arrayListOf()函数可以直接创建一个Java中的ArrayList对象实例
  • 可变集合MutableList
    • 新增了add/addAll,remove/removeAll/removeAt,set,clear,retainAll
    • mutableListOf()函数来创建
    • mutableListOf()函数和直接使用arrayListOf()函数一样都是创建了一个ArrayList类
    • 不可变的List可以通过调用转换函数toMutableList()来将其转换成可变的List
  • 遍历List元素
    • 使用Iterator迭代器(Kotlin中的Iterator功能比较简单,并且只能单向移动)
      • 调用iterator()函数,容器返回一个Iterator实例
      • 调用hasNext()函数检查序列中是否还有元素
      • 第一次调用next()函数,返回序列的第一个元素,依次向后递推 - 使用forEach遍历List元素
      • list.forEach({println(it)})
  • List元素操作函数
    • add/remove 是MutableList的
      • 添加一个元素mutableList.add(4)
      • 在下标为0的位置添加元素0mutableList.add(0,0)
      • 删除元素1mutableList.remove(1)
      • 删除下标为1的元素mutableList.removeAt(1)
      • 删除子集合mutableList.removeAll(listOf(3,4))
      • 添加子集合mutableList.addAll(listOf(1,2,3))
    • set/clear操作
      • 更新设置下标为0的元素值为100mutableList.set(0,100)
      • 清空集合mutableList.clear()
      • 把可变集合转为不可变集合mutableList.toList()
    • retainAll
      • 取两个集合交集mList1.retainAll(mList2)
    • contains(element:T) Boolean
      • 判断集合中是否有指定元素list.contains(1)
    • elementAt(index: Int): T
      • 查找下标对应的元素list.elementAt(6)
      • 查找下标对应元素,如果越界根据方法返回默认值list.elementAtOrElse(7,{0})
      • 查找下标对应元素,如果越界就返回nulllist.elementAtOrNull(7)
    • first()
      • 返回集合第一个元素,如果是空集,抛出异常 list.first()
      • 对应的有针对异常处理的函数firstOrNull():T? emptyList.firstOrNull()
      • 返回符合条件的第一个元素,没有则抛出异常 list.first(it % 2 == 0)
      • 对应的有针对异常处理的函数firstOrNull(oerducate:(T)->Boolean):T?返回符合条件的第一个元素,没有就返回nullist.firstOrNull({it > 100})
    • indexOf(element: T): Int
      • 返回指定下标的元素,没有就返回-1 list.indexOf("c")
    • indexOfFirst(predicate:(T) -> Boolean):Int
      • 返回第一个符合条件的元素下标,没有就返回-1 list.indexOfFirst({it.contains("x")})
    • indexOfLast(predicate:(T) -> Boolean):Int
      • 返回最后一个符合条件的元素下标,没有就返回-1 list.indexOfLast(it.contains("k"))
    • last()
      • 返回集合最后一个元素,空集抛出异常 list.last()
    • last(predicate:(T) -> Boolean): T
      • 返回符合条件的最后一个元素,没有就抛出异常 list.last({it > 10})
      • 对应的针对越界处理的lastOrNull函数,返回符合条件的最后一个元素,没有则返回null list.lastOrNull({it > 10})
    • lastIndexOf(element: T): Int
      • 返回符合条件的最后一个元素,没有就返回-1 list.lastIndexOf("abc")
    • single(): T
      • 该集合如果只剩下一个元素,则返回该元素,否则抛出异常 list.single()
    • single(predicate: (T) -> Boolean): T
      • 返回符合条件的单个元素,否则抛出异常,或超过一个的抛异常 list.single({it == 1})
      • 对应的针对越界处理的singleOrNull函数,返回符合条件的单个元素,否则返回null list.singleOrNull({it == 7})
  • List集合的基本运算函数
    • any()
      • 判断该元素至少有一个元素 list.any()
    • any(predicate:(T) -> Boolean)
      • 判断集合中是否有满足条件的元素 list.any({list > 4})
    • all(predicate: (T) -> Boolean)
      • 判断集合中的元素是否都满足条件,当且仅当该集合中的所有元素都满足条件时,返回true。 list.all({it > 2})
    • none()
      • 判断集合无元素,该集合没有任何元素,返回true list.none()
    • none(predicate:(T) -> Boolean)
      • 判断集合中所有元素都不满足条件 list.none({it % 2 == 1})
    • count()
      • 计算集合中的元素的个数 list.count()
    • count(predicate:(T) -> Boolean)
      • 计算集合中满足条件的元素的个数 list.count({it % 2 == 0})
    • reduce
      • 从第一想到最后一项进行累计运算,首先把第一个元素赋值给累加子accumulator,然后逐次向后取元素累加,新值继续赋值给累加子accumulator = operation(accumulator,iterator.next()),依次类推,最后返回累加子的值 list.reduce({total,s -> total + s})
    • reduceRight
      • 从最后一项到第一项进行累计运算,从右边累计运算的累加子是放在后面的 list.reduceRight({total,s -> s + total})
    • fold(initial:R,operation:(acc:R,T)->R):R
      • 是带初始值的reduce,fold函数给累加子赋了初始值initial list.fold(100,{total,next -> next + total})
      • foldRight 和 reduceRight类似,有初始值
    • forEach(action:(T) -> Unit):Unit
      • 循环遍历元素,元素是it list.forEach{value -> if (value > 7) println(value)}
    • forEachIndexed
      • 带index(下标)的元素遍历 list.forEachIndexed(index,value -> if(value > 8) println("value of index $index is $value,greater than 8"))
    • maxmin
      • 查询最大、最小的元素,空集返回null list.max()
    • maxBy(selector:(T)->R):T?、minBy(selector:(T)->R):T?
      • 获取函数映射结果的最大值、最小值对应的那个元素的值,如果没有则返回null。是通过selector函数的返回值来比较大小,获取最终的最大值和最小值。 list.maxBy({it * (1 - it)})
    • sumBy(selector:(T) -> Int):Int
      • 获取函数映射值的总和 list.sumBy({it * it})
  • 过滤操作函数
    • take(n:Int):List<T>
      • 挑出该集合前n个元素的子集合,如果n=0,返回空集,如果n大于集合size,返回该集合。list.take(10)
    • takeWhile(predicate:(T) -> Boolean):List<T>
      • 挑出满足条件的元素的子集合,从第一个元素开始,判断是否满足predicate为true,如果满足条件的元素就丢到返回ArrayList中,只要遇到任何一个不满足条件,就结束循环,返回list。 list.takeWhile({it % 2 == 0})
      • takeLast
        • 挑出最后n个元素的子集合,从集合倒数n个元素起,取出到最后一个元素的子集合。如果传入0,返回空集。如果传入n大于集合size,返回整个集合。如果传入负数,抛出异常。list.takeLast(2)
    • takeLastWhile(predicate:(T) -> Boolean)
      • 从最后开始挑出满足条件元素的子集合。反向取满足条件的元素,遇到不满足的元素,直接终止循环,并返回子集合。 list.takeLastWhile({it % 2 == 0})
    • drop(n:Int)
      • 去除前n个元素返回剩下的元素的子集合 list.drop(5)
    • dropWhile(predicate:(T)->Boolean)
      • 去除满足条件的元素返回剩下的元素的子集合。去除满足条件的元素,当遇到一个不满足条件的元素时,中止操作,返回剩下的元素子集合。 list.dropWhile({it % 2 == 0})
    • dropLast(n:Int)
      • 从最后去除n个元素 list.dropLast(3)
    • dropLastWhile(predicate:(T) -> Boolean)
      • 从最后去除满足条件的元素 list.dropLastWhile({it % 2 == 0})
    • slice(indices:IntRange)
      • 取开始下标至结束下标元素子集合 list.slice(1..3)
    • slice(indices:Iterable<Int>)
      • 返回指定下标的元素子集合 list.slice(listOf(2,4,6))
    • filterTo(destination:C,predicate:(T) -> Boolean)
      • 过滤出满足条件的元素并赋值给destination。把满足过滤条件的元素组成的子集合赋值给入参destination。 list.filterTo(dest,{it > 3})
    • filter(predicate:(T)->Boolean)
      • 过滤出满足条件的元素组成的子集合。相对于filterTo函数,filter函数更加简单易用。 list.filter({it > 3})
    • filterNot(predicate:(T) -> Boolean)
      • 用来过滤所有不满足条件的元素
    • filterNotNull()
      • 过滤掉null元素
  • 映射操作函数
    • map(transform:(T)->R):List<R>
      • 将集合中的元素通过转换函数transform映射后的结果,存到一个集合中返回。 list.map({it * it})
    • mapIndexed(transform:(kotlin.Int,T) -> R)
      • 转换函数transform中带有下标参数。可以同时使用下标和元素的值来进行转换。 list.mapIndexed({index,it -> index * it})
    • mapNotNull(transform:(T) -> R?)
      • 遍历集合每个元素,得到通过函数算子transform映射之后的值,剔除掉这些值中的null,返回一个无null元素的集合。 list.mapNotNull({it})
    • flatmap(transform:(T)->Iterable<R>):List<R>
      • 在原始集合的每个元素上调用transform转换函数,得到的映射结果组成的单个列表。与map相比,使用map是把list中的每一个元素都映射成一个List-n,然后以这些List-n为元素,组成一个大的嵌套的List返回。而使用flatMap则是把list中的第一个元素映射成一个List1,然后把第二个元素映射成的List2跟List1合并,最终返回一个扁平的List。
  • 分组操作函数
    • groupBy(keySelector:(T) -> K):Map>
      • 将集合中的元素按照条件选择器keySelector分组,并返回Map。words.groupBy({it.length},{it.contains("b")})
    • groupingBy(crossinline keySelector:(T)->K):Grouping
      • 可以通过该函数来创建一个Grouping,然后调用计数函数eachCount统计分组 words.groupingBy({it.first()}).eachCount()
  • 排序操作符
    • reversed():List<T>
      • 倒序排列集合元素 list.reversed()
    • sortedsortedDescending
      • 升序排序和降序排序 list.sorted
    • sortedBysortedByDescending
      • 可变集合MutableList的排序操作,根据函数映射的结果进行升序排序和降序排序 mlist.sortBy({it.length})
  • 生产操作符
    • zip(other:Iterable<R>):List<Pair<T,R>>
      • 两个集合按照下标配对,组合成的每个Pair作为新的List集合中的元素,并返回。如果两个集合长度不一样,取短的长度。 list1.zip(list2)
    • partition(predicate:(T)->Boolean:Pair<List<T>,List<T>>)
      • 根据判断条件是否成立,将集合拆分成两个子集合组成的Pair。 list.partition({it > 5})
    • plus(elements:Iterable<T>):List<T>
      • 合并两个List,这是一个操作符函数,可以用“+”代替。 list.plus(list2) , list1+list2
    • list.plusElement(element:T):List<T>
      • 在集合中添加一个元素。 list.plusElement(10) , list.plus(10)

Set

含有不可变Set和可变MutableSet(支持添加和删除)

  • 空集
    • 空集继承了Serializable,表明是可被序列化的。它的size是0,isEmpty()返回true,hashCode()是0。
  • 创建Set
    • setOf(vararg elements:T):Set<T>
      • 使用元素elements创建一个Set。Set中的元素是不可重复的。toSet()是Array类的扩展函数,可以将Array类转变为Set。
      • 使用toSet()可以对List去重。
      • 使用emptySet<Int>()setOf()来创建空集
    • mutableSetOf():MutableSet<T>
      • 创建一个可变的Set
  • 使用Java中的Set类
    • hashSetOf —— java.util.HashSet 该类按照哈希算法来存取集合中的对象,存取速度较快
    • linkedSetOf() —— java.util.LinkedHashSet
    • mutableSetOf() —— java.util.LinkedHashSet 具有HashSet的查询速度,且内部使用链表维护元素的顺序,在对Set元素进行频繁插入,删除的场景中使用
    • sortedSetOf() —— java.util.TreeSet 该类实现了SortedSet接口,能够对集合中的对象进行排序
  • Set元素中的加减操作plus minus
    • 针对Set做了一些加减运算的扩展函数。 val ms = mutableSetOf(1,3,4,7) , ms + 10 , ms - 1

Map

Map是一种把键对象Key和值对象Value映射的集合。它的每一个元素都包含一对键对象和值对象。 Key可以看成是Value的索引,作为key的对象在集合中不可重复。
Map和Set、List一样,Map也分为只读Map和可变的MutableMap。

  • 创建Map
    • HashMap HashMap是基于哈希表的Map接口的实现,系统会根据hash算法来计算key-value的存储位置,可以通过key快速地存取value。它允许使用null值和null键。
    • TreeMap 使用红黑二叉树的Map接口
    • LinkedHashMap 使用链表实现,保存了记录的插入顺序。
    • mapOf()
      • 创建一个只读空Map mapOf<String,Int>()emptyMap<String,Int>()
    • mapOf(pair:Pair<K,V>):Map<K,V>
      • 使用二元组Pair创建一个只读Map mapOf(1 to "x",2 to "y",3 to "z")
      • map.get(1)
      • map.size
      • map.entries
    • mutableMapOf()
      • 创建一个空的可变的Map,该函数是直接调用的LinkedHashMap()构造函数
    • mutableMapOf(vararg pairs:Pair<K,V>):MutableMap<K,V>
      • 创建一个可编辑的MutableMap对象。如果有重复的key键,后面的会直接覆盖掉前面的
    • hashMapOf():HashMap<K,V>
      • 创建HashMap对象
    • linkedMapOf():LinkedHashMap<K,V>
      • 创建空对象LinkedHashMap
    • linkedMapOf(vararg pairs:Pair<K,V>):LinkedHashMap<K,V>
      • 创建带二元组Pair元素的LinkedHashMap对象
    • sortedMapOf(vararg pairs:Pair<K,V>):SortedMap<K,V>
      • 创建一个根据Key升序排序好的TreeMap
  • 访问Map的元素
    • Map的元素包含的属性有entries、keys、values size,还有一个操作符Map.get(key),等同于使用Map[Key]
      • entries属性 可直接访问entries属性
      • keys 属性
      • values 属性
      • size 属性
      • get(key:K)——对应的操作符的[]。如果这个key不在Map中,就返回null。如果不想返回null,可以使用getOrDefault函数
  • Map操作函数
    • containsKey(key:K):Boolean
      • 是否包含该key
    • containsValue(value:V):Boolean
      • 是否包含该value
    • component1() component2()
      • Map.Entry的操作符函数,分别用来直接访问key和value
    • Map.Entry<K,V>.toPair():Pair<K,V>
      • 把Map的Entry转换为Pair
    • getOrElse(key:K,defaultValue:()->V):V
      • 通过key获取值,当没有值可以设置默认值
    • getValue(key:K):V
      • 当Map中不存在这个key,调用get函数,如果不想返回null,直接抛出异常
    • getOrPut(key:K,defaultValue:()->V):V
      • 如果不存在这个key,就添加这个key到Map中,对应的value是defaultValue
    • iterator():Iterator<Map.Entry<K,V>>
      • 这个函数返回的是entries.iterator()
    • mapKeys(transform:(Map.Entry<K,V>)->R):Map<R,V>
      • 把Map的Key设置为通过转换函数transform映射之后的值 map.mapKeys{it.key * 10}
    • mapValues(transform:(Map.Entry<K,V>)->R):Map<K,R>
      • 对应的这个函数是把Map的value设置为通过转换函数transform转换之后的新值。 map.mapValues({it.value + "$"})
    • filterKeys(predicate:(K) -> Boolean):Map<K,V>
      • 返回过滤出满足key判断条件的元素组成的新Map。 map.filterKeys({it > 0})
    • filterValues(predicate:(V)->Boolean):Map<K,V>
      • 返回过滤出满足value判断条件的元素组成的元素组成的新Map。 map.filterValues({it > "b"})
    • filter(predicate:(Map.Entry<K,V>)->Boolean):Map<K,V>
      • 返回过滤出满足Entry判断条件的元素组成的新Map。 map.filter({it.key > 0 && it.value > "b"})
    • Iterable<Pair<K,V>>.toMap(destination:M):M
      • 把持有Pair的Iterable集合转换为Map。 pairList.toMap()
    • Map<out K,V>.toMutableMap():MutableMap<K,V>
      • 把一个只读的Map转换为可编辑的MutableMap。 map.toMutableMap()
    • put(key:K,value:V):V?
      • 根据key设置元素的value。如果该key存在就更新value,不存在就添加,但是put的返回值是null
    • MutableMap<out K,V>.remove(key:K):V?
      • 根据键值key来删除元素 map.remove(-1)
    • MutableMap<K,V>.clear():Unit
      • 清空MutableMap

泛型

Java中泛型PECS(Producer Extends Consumer Super)原则。

  • 频繁往外读取内容的,适合用上界Extends —— (? extends T)
  • 经常往里插入的,适合用下界Super —— (? super T)

Kotlin中的泛型

  • out T与in T
    out T 等价于 <? extends T> 和 in T 等价于 <? super T>
  • 声明处型变
  • 类型投影

泛型类

1
2
3
class Box<T>(t:T) {
var value = t
}

面向对象编程

类与构造函数

构造函数

  • 主构造函数(是类头的一部分,直接放在类后面。如果没有注解或者可见性修饰符,可以省略。主构造函数不能包含任何代码,初始化代码可以放到以init关键字作为前缀的初始化)
  • 次构造函数(前缀用constructor声明)
  • 私有主构造函数(class DontCreateMe private constructor(){}

类的属性

类的属性必须要初始化或者具有抽象属性,并且kotlin中类的字段自动带有getter方法和setter方法。

接口

使用interface作为接口的关键词

实现接口

接口是没有构造函数的。使用冒号:语法来实现一个接口,如果有多个用逗号隔开

覆盖冲突

如果一个类从他的直接父类继承了同一个函数的多个实现,那么它必须重写这个函数并且提供自己的实现。为表示使用父类中提供的方法,我们用super关键字实现。在重写print()时,比如我们实现的ProjectService、MilestoneService都有一个print()函数,当我们直接使用super.print()时,编译器是无法知道想要调用的是哪个里面的print函数,这个我们叫做覆盖冲突。这个时候,我们使用以下语法来调用super<ProjectService>.print() 或者 super<MilestoneService>.print()

抽象类和接口的差异

概念上的区别

接口主要是对动作的抽象,定义了行为特性的规约。
抽象类是对根源的抽象。
当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。

语法层面上的区别

接口不能保存状态,可以有属性但必须是抽象的。
一个类只能继承一个抽象类,而一个类却可以实现多个接口。
类如果要实现一个接口,它必须实现接口声明的所有方法。但类不可以实现抽象类声明的所有方法。
接口中的所有方法隐含的都是抽象的。而抽象类则可以包含抽象和非抽象的方法。

设计层面上的区别

抽象类是对一种事物的抽象。而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为。而接口却是对类局部行为进行抽象。

继承

在Kotlin中,所有的类会默认继承Any整个父类,但Any并不完全等同于Java中的Object类,因为它只有equals()、hashCode()、toString()这三个方法。

open类

除了抽象类、接口默认可以被继承,我们也可以把一个类声明为open,这样我们就可以继承这个open类。
当我们想定义一个父类时,需要使用open关键字:open class Base{}
当然,抽象类是默认open的,然后在子类中使用冒号进行继承: class SubClass : Base() {}
如果父类有构造函数,那么必须在子类的主构造函数中进行继承,没有的话可以选择主构造函数或者二级构造函数。

1
2
3
4
//父类
open class Base(type:String) {}
//子类
class SubClass(type:String) : Base(type) {}

Kotlin中的override()和Java中也有所不同,因为Kotlin提倡所有的操作都是明确的,因此需要将希望被重写的函数设为open,然后通过override标记实现重写。重写的函数也是open的,如果希望它不被重写, 可以再前面增加final:

1
2
3
4
5
6
open class SubClass : Base {
constructor(type:String) : super(type) {}
final override fun doSomething() {
super.doSomething()
}
}

枚举类

枚举类的基本用法

1
2
3
4
5
enum class Direction {
NORTH,SOUTH,WEST,EAST
}
>>> val north = Direction.NORTH

每个枚举常量都是一个对象。枚举常量用逗号分隔。

初始化枚举值

1
2
3
4
5
enum class Color(val rgb:Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}

另外,枚举常量也可以声明自己的匿名类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum class ActivityLifeState {
onCreate {
override fun signal() = onStart
},
onStart {
override fun signal = onStop
},
onStop {
override fun signal() = onStart
},
onDestroy {
override fun signal() = onDestroy
}
abstract fun signal() : ActivityLifeState
}

使用枚举常量

每个枚举常量,默认都有name 和 ordinal 的属性。

1
2
3
4
5
enum class RGB {RED,GREEN,BLUE}
>>> val rgbs = enumValue<RGB>().joinToString {"${it.name} : ${it.ordinal}"}
>>> rgbs
RED : 0 , GREEN : 1 , BLUE : 2

注解类

元注解

  • @Target(AnnotationTarget.CLASS,AnnotationTarget.FUNCTION,AnnotationTarget.EXPRESSION,AnnotationTarget.FIELD,AnnotationTarget.LOCAL_VARIALBE,AnnotationTarget.TYPE,AnnotationTarget.TYPEALIAS,AnnotationTarget.TYPE_PARAMETER,AnnotationTarget.VALUE_PARAMETER,AnnotationTarget.CONSTRUCTOR) 指定这个注解可用于哪些元素(类、函数、属性、表达式等等)
  • @Retention(AnnotationTarget.SOURCE) 指定这个注解的信息是否被保存到编译后的class文件中,以及在运行时是否可以通过反射访问到它。
  • @Repeatable 允许在单个元素上多次使用同一个注解
  • @MustBeDocumented 表示这个注解是公开API的一部分,在自动产生的API文档中的类或者函数签名中,应该包含这个注解的信息

单例模式

Kotlin中没有“静态属性和方法”,但是也提供了实现类似于“单例”的功能

  • object 单例模式
  • 匿名object 有时候需要的仅仅是一个简单对象,这个时候可以直接使用val Porigin = object{var x = 0.0 var y = 0.0}
  • companion object 伴生对象,一个类只能有一个伴生对象,伴生对象的初始化是在相应的类被加载解析时
  • 如果想使用Java中的静态成员和静态方法
    • @JvmField注解: 生成与该属性相同的静态字段
    • @JvmStatic注解: 在单例对象和伴生对象中生成对应的静态方法
1
2
3
4
5
6
7
object User {
val username : String = "admin"
val password : String = "admin"
}
>>> User.username
>>> User.password

密封类

sealed类是枚举类的扩展,枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例。要声明一个密封类,需要在类名前面添加sealed修饰符。密封类的所有子类都必须与密封类在同一个文件中声明。

使用密封类的主要场景是在使用when表达式的时候,能够验证语句覆盖了所有情况,而无需再添加一个else子句。

数据类

我们写Java代码时,会经常创建一些只保存数据的类。

  • POJO类:普通Java类,具有一部分getter/setter方法就可以被称为POJO
  • DTO类:数据传输类,泛指用于展示层与服务层之间的数据传输对象
  • VO类:ViewObject
  • PO类:持久对象,是由一组属性和属性的get和set方法组成。用来封装原始数据。
  • BO类:业务对象层,表示应用程序领域内“事物”的所有实体类。
  • DO类:领域对象,从现实世界中抽象出来的有形或无形的业务实体

Kotlin中使用data前缀来构建数据类,数据类有以下限制:

  • 主构造函数需要至少有一个参数
  • 主构造函数的所有参数需要标记为val或var
  • 数据类不能是抽象、开放、密封或者内部的(不能用abstract、open、sealed、inner作为前缀修饰),只能是final的

数据类的解构

解构相当于Component函数的逆向映射

1
2
3
val helen = User("Helen","Female",15)
val (name,gender,age) = helen
println("$name,$gender,$age years of age")

输出

1
Helen,Female,15 years of age

嵌套类

类可以嵌套在其他类中,可以嵌套多层。
访问嵌套类对的方式是直接使用“类名.”,有多少层嵌套,就用多少层类名加点号来访问。
普通的嵌套类,没有持有外部类的引用,所以是无法访问外部类的变量的。

内部类

类可以标记为inner以便能访问外部类的成员,这样的类叫内部类。内部类会带有一个对外部类的对象的引用。

匿名内部类

匿名内部类就是没有名字的内部类。既然是内部类,那么它自然也可以访问外部类的变量。比如Thread()、Runnable()。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//first
Thread({
printlin("test")
}).start()
//second
val wait = Runnable {
println("test2")
}
Thread(wait).start()
//third
val wait = {
println("test3")
}
Thread(wait).start()

委托

代理模式

代理模式(Proxy Pattern) 也称为委托模式。在代理模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托(Delegation)给另一个对象来处理。代理模式使得我们可以通过聚合来替代继承,它还使我们可以模拟mixin(混合类型)。委托模式的作用是将委托者与实际实现代码分离出来,以达成解耦的目的。

暂时不理解

函数式编程

函数式编程概述

“函数式编程” 又称泛函编程,是一种“编程范式”,也就是如何编写程序的方法论。它的基础是λ演算。函数式编程的主要思想是把问题的解决方案写成一系列嵌套的函数调用。

在Kotlin中使用函数式编程

Kotlin中的函数

在Kotlin中的函数使用fun关键字声明

1
2
3
fun double(x: Int): Int {
return 2 * x
}