前言
在上一篇中,讲解了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}")
}
注意看前三个变量的定义,str1 与str3 都是直接为大写的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()
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 {
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) 的T 在block 括号里面 - 所以在闭包里
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 用法一
带着解析来撸码试试看:
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{
"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"
}
}
- 运行之前,我们先看
isLong 、showMessage 、println 这三个都是方法 "The people's Republic of china.".run(::isLong) ,这部分将会直接调用isLong 方法- 因为
run函数 使用者是字符串String,所以第一个方法isLong 的形参必须是String字符串! - 因为第一个方法
isLong 返回值为boolean 类型,所以第二个.run(::showMessage) ,形参必须为上一个方法的返回值boolean 类型,并且会执行showMessage 方法。 - 后面的依次类推,有点像责任链模式
带着这样的分析,来看看运行效果:
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("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{
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对应的集合相关的内容进行详解!
|