本篇将讲述Kotlin的一些语法糖和Lambda的使用
Kotlin的一些语法和数据结构
本篇已Kotlin为主要语言
数组
数组
链表
链表通常有单链表和双向链表
单链表
单链表是链表中的一种,它的方向是单向的,对链表的访问要从头部(head)开始,然后依次通过next去访问下一个结点
单链表的数据结构是分为两部分,第一个是元素,第二个是指针。元素用来存储任意数据类型,指针则是指向下一个结点的引用
创建单链表使用的结点
1
2
3class Node(var value : Int) {
var next : Node? = null
}创建单链表
1
2
3
4
5
6class LinkedNode{
//头结点
var head : Node? = null
//尾结点
var last : Node?= null
}
双向链表
双向链表也是链表中的一种,它与单链表不同的是,它的每个结点有两个指针。第一个指针是指向它的前驱结点(也就是指向它的上一个),第二个则是指向它的后继结点(也就是指向它的下一个)。所以双向链表可以很方便的去访问它的前驱结点和后继结点
双向链表的数据结构是由3部分组成,第一部分为prev指针(前驱指针),第二部分为元素,第三部分为next指针(后继指针)。prev指针是指向上一个结点的引用,元素则可以为任意数据类型,next指针则是指向下一个结点的引用
创建双向链表使用的结点
1
2
3
4class BNode(var value : Int) {
var next : BNode? = null
var prev : BNode? = null
}创建双向链表
1
2
3
4class BothwayNode{
var head : BNode? = null
var last : BNode? = null
}
单向循环链表
单向循环链表,它只是在单链表的基础上使得最后一个结点(尾结点)的指针不在指向空,而是指向头部。使其成为一个循环
单向循环链表的数据结构和单链表是一致的
双向循环链表
双向循环链表,它也只是在双链表的基础上使得最后一个结点的后继指针(next)指向头节点,头结点的前驱指针指(prev)向最后一个结点
双向循环链表的数据结构也是与双向链表一致
链表的基础操作
单链表插入元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class LinkedNode {
var head : Node? = null
var last : Node? = null
fun insert(value : Int){
val node = Node(value)//先创建结点
if(head == null){//两种情况,第一种为单链表为空
head = node//则将元素传入,并当前元素即是头也是尾
last = node
}else{
last!!.next = node//如果不为空,则将尾指针指向它,并将尾指针向后移
last = node
}
}
}单链表删除元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24class LinkedNode {
var head : Node? = null
var last : Node? = null
fun delete(value : Int){
if (head==null){
return
}
if(head!!.value == value){//如果头就是要删除的元素
head = head!!.next//将头向后移即可
return
}
var iterator = head//创建一个用来遍历的结点
while(iterator!!.next!=null){//从头到尾循环
if(iterator.next!!.value == value){//当下一个结点就是要被删除的元素时,退出
break
}
//每次都向后移
iterator = iterator.next
}
//当退出循环就代表下一个元素是要被删除,则将指针指向下一个
iterator.next = iterator.next!!.next
}
}双向链表添加元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class BothwayNode {
var head : BNode? = null
var last : BNode? = null
fun insert(value : Int){
val node = BNode(value)
if (head == null){//这里都与单链表相似
head = node
last = node
}else{
//因为双向链表是有前驱和后继指针的
//所以需要将last(尾指针)指向它,在将插入元素的前驱指向last,最后在将last(尾指针)后移
last!!.next = node
node.prev = last
last = node
}
}
}双向链表删除元素
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
32class BothwayNode {
var head : BNode? = null
var last : BNode? = null
fun delete(value : Int){
if (head == null){
return
}
if(head!!.value == value){
//当要删除的元素为头时,将头后移
head = head!!.next
return
}
var iterator = head
while (iterator!!.next != null){
if (iterator.next!!.value == value){
break
}
iterator = iterator.next
}
if (iterator.next!!.next != null){
//判断要删除的元素是否为最后一个
//将要删除元素的后一个的前驱指向当前元素
iterator.next!!.next!!.prev = iterator
}else{
//为最后一个,则只需要将前驱设置为空即可
iterator.next!!.prev = null
}
//完成了前驱,还需要将后继继续指向后一位
iterator.next = iterator.next!!.next
}
}
For
对一个数字进行循环
循环到那个数字(不包括)
1
for(i in 1 until n)//n就是输入的数字
循环到那个数字(包括)
1
for(i in 1..n)//n就是输入的数字
Lambda
内联扩展函数
let
1
2
3
4object.let{
//默认是it来代表,可以通过it来访问其公有的属性和方法
it.todo()
}使用场景
场景一: 最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理。
场景二: 然后就是需要去明确一个变量所处特定的作用域范围内可以使用
1
2
3
4
5object?.let {
it.todo()
it.todo1()
it.todo2()
}with
是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式。
1
2
3with(object){
//todo
}例子展示
1
2
3
4
5
6
7
8
9
10override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
with(item){
holder.tvNewsTitle.text = titleEn//这里省略了this.(原本为this.titleEn)
holder.tvNewsSummary.text = summary
holder.tvExtraInf.text = "难度:$gradeInfo | 单词数:$length | 读后感: $numReviews"
...
}
}可以直接调用with(object)的公有属性
run
1
2
3object.run{
//todo
}run可以说是let和with的结合体,它既可以方便调用,又以最后一行为返回值(或指定的return值)
例子展示
1
2
3
4
5
6
7
8
9override fun onBindViewHolder(holder: ViewHolder, position: Int){
getItem(position)?.run{
holder.tvNewsTitle.text = titleEn
holder.tvNewsSummary.text = summary
...
}
}这里是对上面的with进行改变
apply
1
2
3object.apply{
//todo
}apply和run很像,run是返回最后一行或者指定返回结果。而apply则是对本身进行修改,返回自己
例子展示
1
2
3
4
5
6user.apply{
//这些name都是user中的公有属性
name = "xxx"
age = 10
...
}also
1
2
3object.also{
//todo
}also和let很像,also是返回它本身,let是返回最后一行
例子展示
1
2
3
4val adapter = DetailAdapter().also {
binding.userRandomList.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
binding.userRandomList.adapter = it
}
高阶函数
顶层函数
在Kotlin中,如果将一个函数写在文件源代码的最上方(也就是不属于任何一个类),那么这个函数就是顶层函数
代码展示
1
2
3
4
5
6
7
8
9
10
11package com.laboratory.anyrandom.util
fun toast(msg: String) {
runOnMainThread {
Toast.makeText(App.context, msg, Toast.LENGTH_SHORT).show()
}
}
fun runOnMainThread(runnable: Runnable) {
Handler(Looper.getMainLooper()).post(runnable)
}在上述代码中,toast和runOnMainThread就是顶层函数
使用
在Kotlin中,顶层函数属于包内成员,包内可以直接使用,包外只需要import该顶层函数,即可使用。
1
toast("我是顶层函数")
在同一个包下可以直接访问
顶层属性
既然有顶层方法,应该也有顶层属性。和顶层函数一样,属性也可以放在文件的顶层,不附属与任何一个类。这种属性叫顶层属性。
代码展示
1
2
3package com.laboratory.anyrandom.util
val topAttributes :Int = 100和顶层函数相同,想要在Java中进行访问,那么就必须要在路径上方写上(@file:JvmName(“代表名字”))
1
2
3
4"TopUtils") (
package com.laboratory.anyrandom.util
val topAttributes :Int = 100这样在Java中也可以直接访问
1
TopUtils.getTopAttributes();
当然在Java是通过访问器进行访问的(也就是Get() / Set()方法)
扩展函数
Kotlin可以在无需继承的情况下去扩展一个类,调用则像是内部函数一样调用
代码展示
1
2
3
4
5
6
7
8
9
10
11
12
13/**
* 判断是否是中文字符
*/
fun Char.isChinese(): Boolean {
val unicodeBlock = Character.UnicodeBlock.of(this)
if (unicodeBlock == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
|| unicodeBlock == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
) // 中日韩象形文字
{
return true
}
return false
}声明一个扩展函数,想要一个被扩展的目标。以上面代码为例,这是对Char的扩展,扩展的方法名为isChinese,返回值为Boolean类型
扩展函数只能对Public对象进行扩展,对于Private的成员是无法访问的
扩展函数可以与顶层函数一样,与顶层函数写在同个位置
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"TopUtils") (
package com.laboratory.anyrandom.util
import android.os.Handler
import android.os.Looper
import android.widget.Toast
import com.laboratory.anyrandom.App
//顶层函数
fun toast(msg: String) {
runOnMainThread {
Toast.makeText(App.context, msg, Toast.LENGTH_SHORT).show()
}
}
fun runOnMainThread(runnable: Runnable) {
Handler(Looper.getMainLooper()).post(runnable)
}
//扩展函数
/**
* 判断是否是中文字符
*/
fun Char.isChinese(): Boolean {
val unicodeBlock = Character.UnicodeBlock.of(this)
if (unicodeBlock == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
|| unicodeBlock == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
) // 中日韩象形文字
{
return true
}
return false
}
Lazy
lateinit var: lateinit只用于可变变量(var xxx)
by lazy: lazy只用于不可变变量(val xxx)
1
2
3
4val lazyValue: String by lazy {
println("computed!")
"Hello"
}输出结果
1
2
3
4
5//第一次调用
computed!
Hello
//第二次
Hello第一次调用
get()
会执行已传递给lazy()
的 lambda 表达式并记录结果, 后续调用get()
只是返回记录的结果。