7.1.5.1 Java 与 Kotlin 中的字符串
原文链接: https://kotlinlang.org/docs/java-to-kotlin-idioms-strings.html
7.1.5.1 Java 与 Kotlin 中的字符串
了解如何从 Java String 迁移到 Kotlin String。本指南涵盖 Java 的 StringBuilder、
字符串拼接与拆分、多行字符串、流以及其他主题。
本指南包含在 Java 和 Kotlin 中完成字符串常见任务的示例。它将帮助你从 Java 迁移到 Kotlin,并用真正 Kotlin 的方式编写代码。
拼接字符串
在 Java 中,你可以这样做:
1
2
3
4
| // Java
String name = "Joe";
System.out.println("Hello, " + name);
System.out.println("Your name is " + name.length() + " characters long");
|
在 Kotlin 中,请在变量名前使用美元符号($),把该变量的值插入字符串中:
1
2
3
4
5
6
| fun main() {
// Kotlin
val name = "Joe"
println("Hello, $name")
println("Your name is ${name.length} characters long")
}
|
你可以用花括号把复杂表达式括起来实现插值,例如 ${name.length}。更多信息请参阅字符串模板。
构建字符串
在 Java 中,你可以使用 StringBuilder:
1
2
3
4
5
6
7
| // Java
StringBuilder countDown = new StringBuilder();
for (int i = 5; i > 0; i--) {
countDown.append(i);
countDown.append("\n");
}
System.out.println(countDown);
|
在 Kotlin 中,请使用 buildString()——一个内联函数,它把构建字符串的逻辑作为 lambda 实参接收:
1
2
3
4
5
6
7
8
9
10
| fun main() {
// Kotlin
val countDown = buildString {
for (i in 5 downTo 1) {
append(i)
appendLine()
}
}
println(countDown)
}
|
在底层,buildString 使用与 Java 相同的 StringBuilder 类,你可以在 lambda 中通过隐式 this 访问它。
进一步了解 lambda 编码规范。
用集合元素创建字符串
在 Java 中,你使用 Stream API 来过滤、映射并收集元素:
1
2
3
4
5
6
7
8
9
| // Java
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
String invertedOddNumbers = numbers
.stream()
.filter(it -> it % 2 != 0)
.map(it -> -it)
.map(Object::toString)
.collect(Collectors.joining("; "));
System.out.println(invertedOddNumbers);
|
在 Kotlin 中,请使用 joinToString() 函数,Kotlin 为每个 List 都定义了这个函数:
1
2
3
4
5
6
7
8
| fun main() {
// Kotlin
val numbers = listOf(1, 2, 3, 4, 5, 6)
val invertedOddNumbers = numbers
.filter { it % 2 != 0 }
.joinToString(separator = ";") {"${-it}"}
println(invertedOddNumbers)
}
|
注意: 在 Java 中,如果你希望在分隔符与后续项之间留出空格,需要显式地在分隔符中加入空格。
进一步了解 joinToString() 的用法。
字符串为空白时设置默认值
在 Java 中,你可以使用三元运算符:
1
2
3
4
5
6
7
8
9
10
11
| // Java
public void defaultValueIfStringIsBlank() {
String nameValue = getName();
String name = nameValue.isBlank() ? "John Doe" : nameValue;
System.out.println(name);
}
public String getName() {
Random rand = new Random();
return rand.nextBoolean() ? "" : "David";
}
|
Kotlin 提供了内联函数 ifBlank(),它把默认值作为实参接收:
1
2
3
4
5
6
7
8
9
10
| // Java
import kotlin.random.Random
fun main() {
val name = getName().ifBlank { "John Doe" }
println(name)
}
fun getName(): String =
if (Random.nextBoolean()) "" else "David"
|
替换字符串首尾的字符
在 Java 中,你可以使用 replaceAll() 函数。此时 replaceAll() 接收正则表达式 ^## 和 ##$,它们分别表示以 ## 开头和结尾的字符串:
1
2
3
4
| // Kotlin
String input = "##place##holder##";
String result = input.replaceAll("^##|##$", "");
System.out.println(result);
|
在 Kotlin 中,请使用带字符串分隔符 ## 的 removeSurrounding() 函数:
1
2
3
4
5
6
| fun main() {
// Kotlin
val input = "##place##holder##"
val result = input.removeSurrounding("##")
println(result)
}
|
替换匹配内容
在 Java 中,你可以使用 Pattern 和 Matcher 类,例如对某些数据做脱敏:
1
2
3
4
5
6
7
| // Kotlin
String input = "login: Pokemon5, password: 1q2w3e4r5t";
Pattern pattern = Pattern.compile("\\w*\\d+\\w*");
Matcher matcher = pattern.matcher(input);
String replacementResult = matcher.replaceAll(it -> "xxx");
System.out.println("Initial input: '" + input + "'");
System.out.println("Anonymized input: '" + replacementResult + "'");
|
在 Kotlin 中,你使用简化正则表达式操作的 Regex 类。此外,使用多行字符串可以减少反斜杠数量,从而简化正则模式:
1
2
3
4
5
6
7
8
| fun main() {
// Kotlin
val regex = Regex("""\w*\d+\w*""") // multiline string
val input = "login: Pokemon5, password: 1q2w3e4r5t"
val replacementResult = regex.replace(input, replacement = "xxx")
println("Initial input: '$input'")
println("Anonymized input: '$replacementResult'")
}
|
拆分字符串
在 Java 中,要用句点字符(.)拆分字符串,你需要使用转义(\\)。这是因为 String 类的 split() 函数接收正则表达式作为实参:
1
2
| // Kotlin
System.out.println(Arrays.toString("Sometimes.text.should.be.split".split("\\.")));
|
在 Kotlin 中,请使用 Kotlin 的 split() 函数,它把分隔符作为可变数量参数接收:
1
2
3
4
| fun main() {
// Kotlin
println("Sometimes.text.should.be.split".split("."))
}
|
如果你需要用正则表达式拆分,请使用接收 Regex 作为实参的重载版本 split()。
截取子字符串
在 Java 中,你可以使用 substring() 函数,它接收要开始截取子串的字符起始索引(含该索引)。要截取该字符之后的子串,你需要把索引加一:
1
2
3
4
| // Java
String input = "What is the answer to the Ultimate Question of Life, the Universe, and Everything? 42";
String answer = input.substring(input.indexOf("?") + 1);
System.out.println(answer);
|
在 Kotlin 中,你使用 substringAfter() 函数,不需要计算想在其后截取子串的那个字符的索引:
1
2
3
4
5
6
| fun main() {
// Kotlin
val input = "What is the answer to the Ultimate Question of Life, the Universe, and Everything? 42"
val answer = input.substringAfter("?")
println(answer)
}
|
此外,你还可以截取某字符最后一次出现之后的子串:
1
2
3
4
5
6
| fun main() {
// Kotlin
val input = "To be, or not to be, that is the question."
val question = input.substringAfterLast(",")
println(question)
}
|
使用多行字符串
在 Java 15 之前,有几种创建多行字符串的方式。例如使用 String 类的 join() 函数:
1
2
3
4
5
6
| // Java
String lineSeparator = System.getProperty("line.separator");
String result = String.join(lineSeparator,
"Kotlin",
"Java");
System.out.println(result);
|
Java 15 中出现了文本块。有一点需要注意:如果你打印多行字符串,而三引号写在下一行,输出中就会多出一个空行:
1
2
3
4
5
6
| // Java
String result = """
Kotlin
Java
""";
System.out.println(result);
|
输出:

如果你把三引号写在最后一个词所在的同一行,这种差异就会消失。
在 Kotlin 中,你可以把引号写在新的一行,输出中不会多出空行。任意一行最左侧的字符决定该行的起始位置。与 Java 的区别在于,Java 会自动裁剪缩进,而在 Kotlin 中你需要显式地做这件事:
1
2
3
4
5
6
7
8
| fun main() {
// Kotlin
val result = """
Kotlin
Java
""".trimIndent()
println(result)
}
|
输出:

如果要得到额外的空行,你应该显式地把这个空行加入多行字符串。
在 Kotlin 中,你也可以使用 trimMargin() 函数来自定义缩进:
1
2
3
4
5
6
7
8
| // Kotlin
fun main() {
val result = """
# Kotlin
# Java
""".trimMargin("#")
println(result)
}
|
进一步了解多行字符串。
接下来学什么?
如果你有喜欢的惯用法,欢迎通过发送 pull request 与我们分享。