Android—Kotiln基础教程(三)

前言

在上一篇中,讲解了Kotlin对null处理以及自定义异常,在本篇中,将会以字符串、数字类型、标准库函数对Kotlin进行讲解。

1. 字符串

1.1 分割字符串

示例一

fun main() {
    
    
    val name="Jimmy's friend"
    var index=name.indexOf("\'")
    val str=name.substring(0 until index )
    println(str)
}

运行效果


Jimmy

这没啥好说的,和java差不多,直接到示例二。

fun main{
    
    
    val names = "jack,bob,tom,jason"
    val data = names.split(",")
    var name1 = data[0]
    var name2 = data[1]
    println(name1)
    println(name2)
    var (origin, dest, proxy) = names.split(",")
    println("-----------------")
    println(origin)
    println(dest)
    println(proxy)
}

运行效果

jack
bob
-----------------
jack
bob
tom

上面那部分代码也没啥可说的,主要关注下面,可定义多个变量可同时按顺序给数组的每一个变量赋值

1.2 字符串替换

fun main{
    
    
    val str1 = "The people's Republic of china"
    val str2= str1.replace("p","a")
    println(str1)
    println(str2)
    println("-----------------------------")
    val str3 = str1.replace(Regex("[aesou]")) {
    
    
        when (it.value) {
    
    
            "a" -> "1"
            "e" -> "5"
            "s" -> "6"
            "o" -> "3"
            else -> it.value
        }
    }
    println(str1)
    println(str3)
}

运行效果

The people's Republic of china
The aeoale's Reaublic of china
-----------------------------
The people's Republic of china
Th5 p53pl5'6 R5public 3f chin1

从这个运行效果上看,分割线以上的,可实现单种字符串替换;分割线以下的可实现多种字符串组合替换。

1.3 字符串比较

fun main{
    
    
    val str1="Tom"
    val str2="tom".capitalize()
    val str3="Tom"
    println("$str1,$str2,$str3")
    println("str1==str2 ${
      
      str1==str2} ; str1==str3 ${
      
      str1==str3}")
    println("str1===str2 ${
      
      str1===str2} ; str1===str3 ${
      
      str1===str3}")
}

注意看前三个变量的定义,str1str3都是直接为大写的Tom,而str2为小写的tom但是调用了capitalize方法转换成了大写的Tom,后面进行了=====相互比较,来看看运行效果:

Tom,Tom,Tom
str1==str2 true ; str1==str3 true
str1===str2 false ; str1===str3 true

从这个运行结果可知,如果值相同,那么==比较的内容时相同的,不过===就不一定了,即使str2通过capitalize方法将字符串首字母转成大写,强迫值与str1相同,但str2的地址与str1不同。

所以说==比较的 是两个变量的值;===比较的是两个变量的物理地址!

1.4 字符串遍历

fun main{
    
    
    val names = "jack,bob,tom,jason"
    val data = names.split(",")
    data.forEach {
    
    
        println(it)
    }
    println("-------------------")
    "The people's Republic of china".forEach {
    
    
        print("$it *")
    }
    println("")
}

运行效果

jack
bob
tom
jason
-------------------
T *h *e *  *p *e *o *p *l *e *' *s *  *R *e *p *u *b *l *i *c *  *o *f *  *c *h *i *n *a *

这没啥可说的,都挺简单,不过注意看,这里的 forEach闭包里使用了it,从这个运行效果看,貌似为循环对象的单个对象个体。来看看它的源码是怎样的!

public inline fun CharSequence.forEach(action: (Char) -> Unit): Unit {
    
    
    for (element in this) action(element)
}

我们先看fun后面的CharSequence.forEach中的CharSequence,它可以表示一个字符串String。众所周知一个字符串由多个字符(char)组成。于是接着看后面的action: (Char) -> Unit,这一眼就能看出是一个方法,形参为Char返回值为Unit,也就是java中的void

因为形参为Char所以在forEach闭包里面的it就为String的单个对象char

2. 数字类型

2.1 类型强转

fun main{
    
    
    val number1: Int? = "8.95".toIntOrNull() // 不能转换就直接 为null
    println(number1)
}

运行效果

null

这很简单,就直接调用对应的api就行了。

2.2 四舍五入

fun main{
    
    
    val s="%.2f".format(8.6566542156) //四舍五入
    println(s)
    println(8.956215.toInt()) //去掉小数点后位取整
    println(8.456215.roundToInt())//四舍五入法取整
}

运行效果

8.66
8
8

这些简单,直接过。

3. 标准库函数

3.1 apply函数

学习新函数我们先看看这个函数的实现,看它里面长什么样子。

@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    
    
    contract {
    
    
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}
  • 这里的<T>表示声明泛型,
  • T.apply(): T这个T代表使用这个apply函数的对象,: T这个表示函数执行完成后也会返回当前使用者对象。
  • block: T.() -> Unit 这个表示在函数闭包里,可使用当前调用apply函数的对象

分析完了,我们来撸码验证下:

fun main{
    
    
    val file=File("d://i have a dream.txt")
    file.setExecutable(true)
    file.setReadable(true)
    file.setWritable(true)
    val file2=File("d://i have a dream.txt").apply {
    
    
        //apply里面的this=外面的成员变量 file2
        // eg: this.setExecutable=外面的file2.setExecutable
        this.setExecutable(true)
        setReadable(true)
        setWritable(true)
    }
    file2.setExecutable(true)
}

这个代码可知,可以在apply闭包里,将调用者作为当前对象使用,并且能够轻易访问其属性。

3.2 let函数

上一篇中已经分析过let源码,这里就不再次分析源码了。

fun main{
    
    
    val result = listOf(3, 2, 1).lastIndexOf(1).let {
    
    
        it * it
    }
    println(result)
    println(formatGreTing("bob"))
    println(formatGreTing(null))
    println(formatGreTing2("bob"))
    println(formatGreTing2(null))
}

fun formatGreTing(questName: String?): String {
    
    
    return questName?.let {
    
    
        "Welcome, $it."
    } ?: "What's your name"
}

fun formatGreTing2(questName: String?): String {
    
    
    return if (questName != null) {
    
    
        "Welcome , $questName"
    } else {
    
    
        "What's your name"
    }
}
  • 这里先是通过listOf(3, 2, 1).lastIndexOf(1)确定了集合倒数第二个元素2
  • 然后使用let函数进行了乘法运算。
  • 后面的两个方法使用了?.let表示如果为空则不执行let闭包里的内容,
  • 后面紧跟着?: "What's your name",表示如果questName?.let为空则默认为后面字符串。

运行效果

4
Welcome, bob.
What's your name
Welcome , bob
What's your name

3.3 run函数

还是一如既往的先看源码实现:

@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    
    
    contract {
    
    
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

咋一看,咋和let源码的相似呢,但仔细一看,还是有不同的地方!

  • 在 let函数里<T, R> T.let(block: (T) -> R): R这里面的block: (T)Tblock括号里面
  • 所以在闭包里it才是的对应的使用者!
  • 而在run函数里<T, R> T.run(block: T.() -> R): R这里面的block: T.()T``block括号前面
  • 所以在闭包里this才是对应的使用者!
  • 上面的apply函数<T> T.apply(block: T.() -> Unit): T依是如此!

所以在这就能看出apply函数run函数两者的区别了!

  • 相同点:apply函数run函数 闭包里都是以this指向当前使用者对象
  • 不同点:apply函数 闭包结束后返回的是当前使用者对象;而run函数 将会以闭包最后一句表达式作为返回值

3.3.1 用法一

带着解析来撸码试试看:

    /**
     *  光看作用域run 和 apply 差不多,但与 apply 不同,run不返回接收者,run 返回的是最后一项表达式
     */
    val file = File("d://i have a dream.txt")
    val result = file.run {
    
    
        readText().contains("打")
    }
    println(result)

记得这里要创建对应文件哈,里面可以随意写入内容。运行效果:


true

在闭包里readText().contains这意思是:读取该文件判断是否含有对应字符,也就是bool类型变量,所以打印也为bool类型的值。因为我这里面还有这个字符,所以结果就为true。

当然run函数还有另一种用法:执行函数引用

3.3.2 用法二

fun main{
    
    
    /**
     * run 也能用来执行函数引用 
     *
     * run 作为 作用域 后面跟{} ; 作为函数引用后面则用 ()
     *
     */

    "The people's Republic of china.".run(::isLong).run(::println)
    "The people's Republic of china.".run(::isLong).run(::showMessage).run(::println)
}

fun isLong(name: String) = (name.length >= 10)

fun showMessage(isLong: Boolean): String {
    
    
    return if (isLong) {
    
    
        "Name is too long"
    } else {
    
    
        "Please rename"
    }
}
  1. 运行之前,我们先看isLongshowMessageprintln这三个都是方法
  2. "The people's Republic of china.".run(::isLong),这部分将会直接调用isLong方法
  3. 因为run函数使用者是字符串String,所以第一个方法isLong的形参必须是String字符串!
  4. 因为第一个方法isLong返回值为boolean类型,所以第二个.run(::showMessage),形参必须为上一个方法的返回值boolean类型,并且会执行showMessage方法。
  5. 后面的依次类推,有点像责任链模式

带着这样的分析,来看看运行效果:

true
Name is too long

运行效果和上面分析的一致。又掌握一种函数的用法。接下来继续新的函数。

3.4 with函数

要养成先看源码的习惯,知其原理后,运用时才会得心应手!

@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    
    
    contract {
    
    
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}
  • 大致感觉这个函数和run函数类似,
  • with函数为:fun <T, R> with(receiver: T, block: T.() -> R): R
  • run函数为:fun <T, R> T.run(block: T.() -> R): R
  • 仔细对比下,还是能看出细节差别,run函数是通过使用者.run的方式使用的;
  • with函数却是将使用者当成方法第一个形参的方式使用。

既然知道了该方法的原理,那么将其原理带入方法里尝试一下:

fun main{
    
    
    /**
     * with 函数 是 run的变体 ,他们的功能行为是一样的,但with的调用方式不同,
     * 调用with时 需要【值参作为第一个参数传入】
     *
     * 当前列子 this ="The people's Republic of China."
     */
    with("The people's Republic of China.") {
    
     this.length }.run(::println)
}

直接运行,看看效果


31

完美运行。直接开始下一个函数。

3.5 takeIf函数

还是先看源码!

public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
    
    
    contract {
    
    
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (predicate(this)) this else null
}
  • 还是先看外部fun <T> T.takeIf(): T?这部分代码,声明泛型,然后通过这泛型使用了方法takeIf
  • 接着看: T?这个意思就是,返回值可能为当前使用者,也有可能为空
  • predicate: (T) -> Boolean 这个意思是形参为当前使用者,返回值为boolean类型
  • predicate: (T) ,因为使用者在括号里面,所以在闭包里,当前使用者为it
  • return if (predicate(this)) this else null,通过这句代码可知,如果这个方法返回false那么就会为空,否则为当前使用者对象。

到现在,相信你分析源码的速度越来越快了,甚至以后直接看到源码就能知道它要表达什么意思。没错!就是要练到那种程度!

接着撸码玩玩看:

fun main{
    
    
    /**
     * takeIf  函数 需要判断 lambda 中提供的条件表达式,给出true或false结果,
     * 如果判断结果为true,从takeIf函数返回接收者对象,如果false,则返回为null。
     *
     * 如果【需要判断某个条件是否满足,再决定是否可以复制变量或执行某项任务】,takeIf就非常有用
     */
    var hasChar: Int? =
        File("d://i have a dream.txt")
            .takeIf {
    
     it.canRead() && it.canWrite() }
            ?.run {
    
    
                readText().length
            }
    println(hasChar)
}

源码分析了,再来看读代码,相信你也能够轻松阅读了吧!

  • takeIf闭包里的表达式如果为true,takeIf闭包将会返回当前对象,
    • 那么肯定不为空就会继续调用run函数,将run函数闭包的返回值打印出来。
  • takeIf闭包里的表达式如果为false,takeIf闭包将会返回null,
    • 因为有?.加上闭包返回为null,所以就不会调用后面的 run函数

来看看运行效果:

24

完美运行!

OK,到这里,本篇内容差不多结束了!

结束语

到这里相信你对Kotlin对应的字符串、数字类型、源码解读以及标准库函数有了一定认知。在下一篇中,将会以Kotlin对应的集合相关的内容进行详解!

Guess you like

Origin blog.csdn.net/qq_30382601/article/details/121227089