kotlin 字符串和类型别名

2,282 阅读9分钟

Kotlin 使用 String 代表字符串。字符串表示一个有序的字符集合,例如“google.com”、 ”好好学习”等都代表字符串,字符串也可被当成多个字符的有序集合。

1 字符串类型

String 类型是一种快速、现代化的字符串实现,字符串中的每一个字符都由独立的 Unicode 字符组成, String 允许通过形如 s[i] 的格式来访问字符串指定索引处的字符,也可通过 for 循环遍历字符串中的每一个字符。

fun main(args: Array<String>) {
    var str = "google.com"
    println(str[2]) // 输出 o
    println(str[3]) // 输出 g
    println(str[6]) // 输出 .
    // 遍历 str 字符串中的每一个字符
    for (c in str) {
        println(c)
    }
}

kotlin 的字符串有两种字面值,分别如下:

  • 转义字符串:转义字符串可以有转义字符,转义字符串很像 Java 字符串。
  • 原始字符串:原始字符串可以包含换行和任意文本,原始字符串需要用3 个引号引起来

示例如下:

fun main(args: Array<String>) {
    // 定义普通字符串
    var str = "alibaba.com"
    println("长度为:"+str.length)
    println("----------------")

    // 定义原始字符串
    val txt = """
		|无聊望见了犹豫
		|达到理想不太易
		|即使有信心
		|斗志却抑制
	""".trimMargin()
    println(txt)
    println("----------------")

    // 定义原始字符串
    val txt2 = """
		^海阔天空
		^今天我
		^寒夜里看雪飘过
		^怀着冷却了的心窝飘远方
	""".trimMargin("^")
    println(txt2)
    println("----------------")

    // 定义原始字符串
    val txt3 = """真的爱你
	  |	无法可修饰的一对手
    ^  带出温暖永远在背后
		$    纵使啰嗦始终关注不懂珍惜太内疚
	"""
    println(txt3)
}

输出:

长度为:11
----------------
无聊望见了犹豫
达到理想不太易
即使有信心
斗志却抑制
----------------
海阔天空
今天我
寒夜里看雪飘过
怀着冷却了的心窝飘远方
----------------
真的爱你
	  |	无法可修饰的一对手
    ^  带出温暖永远在背后
		$    纵使啰嗦始终关注不懂珍惜太内疚

kotlin 原始字符串,用 3 个引号引起来的部分都是字符串内容,包括换行符等各种特殊字符。

编程时考虑到程序格式,往往会在原始字符串中进行一些缩进,但这些缩进并不是原始字 符串希望包含的,此时即可使用 trimMargin() 方法来去掉原始字符串前面的缩进一一在默认情况下, kotlin 使用竖线(|)作为边界符。也就是说,所有竖线(|)之前的内容都会被去掉。

如果有必要,开发者也可使用其他字符作为边界符, 此时就需要在 trimMargin() 方法中传入该边界符作为参数。比如示例中传入了 “^”

2 字符串模板

kotlin 允许在字符串中嵌入变量或表达式,只要将变量或表达式放入 ${} 中即可,这样 kotlin 将会把该变量或表达式的值嵌入该字符串中。

fun main(args: Array<String>) {
    val bookPrice = 79
    // 在字符串模板中嵌入变量
    var s = "图书价格是:${bookPrice}"
    println(s)

    val rand = java.util.Random() // 创建 Java 的 Random对象
    // 在字符串模板中嵌入方法调用
    var s2 = "伪随机数是:${rand.nextInt(10)}"
    println(s2)

    val myStr = "flutter"
    println("${myStr}的长度是:${myStr.length}")
    
    var bookName = "金瓶梅"
    val poem = """
		|图书名是${bookName},
		|图书价格是${bookPrice},
		|图书售价是${rand.nextInt(10) + 50}
	""".trimMargin()
    println(poem)
}

输出:

图书价格是:79
伪随机数是:2
flutter的长度是:7
图书名是金瓶梅,
图书价格是79,
图书售价是50

3 字符串方法

Kotlin 的 String 与 Java 的 String 并不是同一个类,因此它们的方法略有不同,但是这两个类所提供的功能大致相似。实际上, Kotlin 的 String 类提供了更多的方法,如提供了 一系列 toXxx() 方法将字符串转换成数值。

3.1 字符串转换

fun main(args: Array<String>) {
    val s1 = "2.34"
    // 将 sl 字符串转换成 Double 类型
    val d: Double = s1.toDouble()
    println(d)

    var s2 = "45"
    // 将 s2 字符串转换成 Int 类型
    var i: Int = s2.toInt()
    println(i)

    val str = "tencent.com"
    println(str.capitalize()); // 首字母大写
    println(str.decapitalize()); // 首字母小写

    var str2 = "360.com"
    // 返回两个字符串相同的前缀
    println(str2.commonPrefixWith("360"));
    // 返回两个字符串相同的后缀
    println(str2.commonSuffixWith("com"));

    var str3 = "java886"
    // 判断 str3 是否包含 3 个连续的数字
    println(str3.contains(Regex("\\d{3}")))
}

输出:

2.34
45
Tencent.com
tencent.com
360
com
true

3.2 字符串查找

fun main(args: Array<String>) {
    val sourceStr = "There is a string accessing example."

    val len = sourceStr.length //获得字符串长度
    val ch = sourceStr[16]  //获得索引位置16的字符

    //查找字符和子字符串
    val firstChar1 = sourceStr.indexOf('r')
    val lastChar1 = sourceStr.lastIndexOf('r', ignoreCase = true)
    val firstStr1 = sourceStr.indexOf("ing")
    val lastStr1 = sourceStr.lastIndexOf("ing")
    val firstChar2 = sourceStr.indexOf('e', 15)
    val lastChar2 = sourceStr.lastIndexOf('e', 15)
    val firstStr2 = sourceStr.indexOf("ing", 5)
    val lastStr2 = sourceStr.lastIndexOf("ing", 5)

    println("原始字符串:" + sourceStr)
    println("字符串长度:" + len)
    println("索引16的字符:" + ch)
    println("从前往后查找r字符,第一次找到它所在索引:" + firstChar1)
    println("从后往前查找r字符,第一次找到它所在索引:" + lastChar1)
    println("从前往后查找ing字符串,第一次找到它所在索引:" + firstStr1)
    println("从后往前查找ing字符串,第一次找到它所在索引:" + lastStr1)
    println("从索引为15位置开始,从前往后查找e字符,第一次找到它所在索引:" + firstChar2)
    println("从索引为15位置开始,从后往前查找e字符,第一次找到它所在索引:" + lastChar2)
    println("从索引为5位置开始,从前往后查找ing字符串,第一次找到它所在索引:" + firstStr2)
    println("从索引为5位置开始,从后往前查找ing字符串,第一次找到它所在索引:" + lastStr2)
}

输出:

原始字符串:There is a string accessing example.
字符串长度:36
索引16的字符:g
从前往后查找r字符,第一次找到它所在索引:3
从后往前查找r字符,第一次找到它所在索引:13
从前往后查找ing字符串,第一次找到它所在索引:14
从后往前查找ing字符串,第一次找到它所在索引:24
从索引为15位置开始,从前往后查找e字符,第一次找到它所在索引:21
从索引为15位置开始,从后往前查找e字符,第一次找到它所在索引:4
从索引为5位置开始,从前往后查找ing字符串,第一次找到它所在索引:14
从索引为5位置开始,从后往前查找ing字符串,第一次找到它所在索引:-1

3.3 字符串比较

fun main(args: Array<String>) {
    val s1 = "Hello"
    val s2 = "Hello"

    // 比较字符串内容是否相等
    println(s1.equals(s2))   //输出true
    println(s1 == s2)       //输出true

    val s3 = "HELlo"
    // 忽略大小写比较字符串内容是否相等
    println(s1.equals(s3, ignoreCase = true))  //输出true
    println(s1 == s3)       //输出false

    // 比较大小
    val s4 = "java"
    val s5 = "Kotlin"

    println(s4.compareTo(s5))    // 输出31
    println(s4.compareTo(s5, ignoreCase = true))   // 输出-1

    // 判断文件夹中文件名
    val docFolder = arrayOf("java.docx", "JavaBean.docx", "Objecitve-C.xlsx", "Swift.docx ")
    var wordDocCount = 0
    // 查找文件夹中Word文档个数
    for (doc in docFolder) {
        // 比较后缀是否有.docx字符串
        if (doc.endsWith(".docx")) {
            wordDocCount++
        }
    }
    println("文件夹中Word文档个数是: " + wordDocCount) // 输出 文件夹中Word文档个数是: 2

    var javaDocCount = 0
    // 查找文件夹中Java相关文档个数
    for (doc in docFolder) {
        // 比较前缀是否有java字符串
        if (doc.startsWith("java", ignoreCase = true)) {
            javaDocCount++
        }
    }
    println("文件夹中Java相关文档个数是:" + javaDocCount) // 输出 文件夹中Java相关文档个数是:2
}

3.4 字符串截取

fun main(args: Array<String>) {
    val sourceStr = "There is a string accessing example."
    // 截取example.子字符串
    val subStr1 = sourceStr.substring(28)  // 输出 example.
    // 截取string子字符串
    val subStr2 = sourceStr.substring(11, 17)      // 输出 string
    
    println(subStr1)
    println(subStr2)
}

3.5 可变字符串 StringBuilder

Kotlin 提供可变字符串类是 kotlin.text.StringBuilder,可变字符串在追加、删除、修改、插入和拼接等操作不会产生新的对象。

fun main(args: Array<String>) {
    // 字符串长度length和字符串缓冲区容量capacity
    val sb1 = StringBuilder()
    println("字符串长度:" + sb1.length)       // 输出:字符串长度:0
    println("字符串容量:" + sb1.capacity())   // 输出:字符串容量:16

    val sb2 = StringBuilder("Hello")
    println("字符串长度:" + sb2.length)      // 输出:字符串长度:5
    println("字符串容量:" + sb2.capacity())  // 输出:字符串容量:21

    // 字符串缓冲区初始容量是16,超过之后会扩容
    val sb3 = StringBuilder()
    for (i in 0..16) {
        sb3.append(8)
    }
    println("字符串长度:" + sb3.length)      // 输出:字符串长度:17
    println("字符串容量:" + sb3.capacity())  // 输出:字符串容量:34
}

字符串追加、插入、删除和替换

fun main(args: Array<String>) {
    //添加字符串、字符
    val sb1 = StringBuilder("Hello")
    sb1.append(" ").append("World")
    sb1.append('.')
    println(sb1)                 // 输出:Hello World.

    val sb2 = StringBuilder()
    val obj: Any? = null
    //添加布尔值、转义符和空对象
    sb2.append(false).append('\t').append(obj)
    println(sb2)                // 输出:false	null

    //添加数值
    val sb3 = StringBuilder()
    for (i in 0..9) {
        sb3.append(i)
    }
    println(sb3)                // 输出:0123456789
    // 插入字符串
    sb3.insert(4, "Kotlin")
    println(sb3)                // 输出:0123Kotlin456789
 
    // 删除字符串
    sb3.delete(1, 2)//删除"1"字符
    println(sb3)                // 输出:023Kotlin456789

    // 删除替换字符串
    sb3.replace(3, 9, "A") // "A"替换"Kotlin"
    println(sb3)                // 输出:023A456789
}

4 类型别名

Kotlin 提供了类似于 C 语言中的 typedef 的功能 : 可以为己有的类型指定另一个可读性更 强的名字。 Kotlin 提供了 typealias 来定义类型别名。 typealias 语句的语法格式为 :

typealias 类型别名=已有类型

如果类型名太长,我们可以使用较短的新名称来替代原类型名。这样有助于缩短较长的泛型类型。例如,通常缩短集合类型是很有吸引力的。

//为 Set<Network . Node>指定更短的别名: NodeSet
typealias NodeSet = Set<Network.Node>

//为 MutableMap<K, MutableList<File>指定更短的别名: FileTable<K> 
typealias FileTable<K> = MutableMap<K, MutableList<File>>

接下来即可直接使用 NodeSet 和 FileTable命名变量。

var set: NodeSet
var table: FileTable<String>

很多时候,我们也可以通过定义别名为内部类起一个更短的名字。例如如下程序。

class A {
    inner class Inner
}

class B {
    inner class Inner
}
// 为 A.Inner 内部类指定别名
typealias AInner = A.Inner

// 为 B.Inner 内部类指定别名
typealias BInner = B.Inner

// 为(T)→ Boolean 类型指定别名 Predicate<T>
typealias Predicate<T> = (T) -> Boolean

fun main(args: Array<String>) {
    // 使用 AInner 定义变量、调用对象
    var a: AInner = A().AInner()
    // 使用 BInner 定义变盘、调用对象
    var b = B().BInner()
    // 使用 Predicate<String>定义变量, 该变量的值是一个 Lambda 表达式
    val p: Predicate<String> = { it.length > 4 }
    // 为 filter ()方法传入 p 参数,只保留长度大于 4 的字符串
    println(arrayOf("Java", "Objective-C", "Go", "Kotlin").filter(p))
}

上面程序中为 A.Inner、B.Inner 两个内部类分别指定了别名 AInner和 BInner , 接下来程序即可使用 Alnner、 Blnner 别名类定义变量、创建对象。

此外, kotlin 自己也大量利用了别名这个功能。比如 kotlin 利用别名建立了 kotlin 类和 Java 类之间的关系 。如下代码是 kotlin 集合体系中定义的别名。

typealias kotlin.ArrayList<E> = java.util.ArrayList<E>
typealias kotlin.HashMap<K, V> = java.util.HashMap<K, V>
typealias kotlin.HashSet<E> = java.util.HashSet<E>
typealias kotlin.LinkedHashMap<K, V> = java.util.LinkedHashMap<K, V>
typealias kotlin.LinkedHashSet<E> = java.util.LinkedHashSet<E>