1. 概述
本文将深入讲解 Kotlin 中的字符串模板(String Templates),包括其基本用法、高级特性以及在多行字符串中处理 $
符号的常见踩坑场景。
如果你还不熟悉 Kotlin 的基础语法或想系统学习,可以参考我们的 Kotlin 入门教程。
2. Kotlin 中的字符串基础
与 Java 一样,Kotlin 的 String
是不可变的。一旦创建,就不能修改其内容,但可以通过操作生成新的字符串实例。
Kotlin 在 JVM 的 String
类基础上扩展了大量实用方法,显著提升了开发效率。
✅ 示例:使用 padEnd()
方法补全字符串长度:
"Hello".padEnd(10, '!')
执行结果为 "Hello!!!!!"
—— 将原字符串右侧填充至总长 10,不足部分用 !
补齐。
这类增强功能让字符串格式化更直观,无需依赖 StringBuilder
或第三方库。
3. 字符串模板详解
字符串模板是指包含嵌入式表达式的字符串字面量,它允许你在双引号内直接插入变量或表达式,实现类似“插值”的效果。
基本语法
对比 Java 写法:
String message = "n = " + n;
Kotlin 更简洁:
val message = "n = $n"
👉 只需在变量名前加 $
,即可完成自动拼接。
支持任意 Kotlin 表达式
通过 ${}
语法,可嵌入任意合法的 Kotlin 表达式:
val message = "n + 1 = ${n + 1}"
甚至支持条件逻辑:
val message = "$n is ${if (n > 0) "positive" else "not positive"}"
⚠️ 注意:虽然 Kotlin 中很多结构是表达式(如 if
、when
),但并非全部都适用。此处能正常工作是因为 if
在 Kotlin 中本身就是表达式,会返回一个值。
由于 ${}
内部是完整表达式上下文,因此里面的双引号不需要转义 —— 它们属于另一个字符串字面量。
模板解析机制
字符串模板的求值过程如下:
- 计算
${}
中的表达式; - 调用结果对象的
toString()
方法; - 将结果插入原位置。
模板嵌套(不推荐)
理论上支持深度嵌套:
val message = "$n is ${if (n > 0) "positive" else
if (n < 0) "negative and ${if (n % 2 == 0) "even" else "odd"}" else "zero"}"
但这种写法可读性差,容易出错 ❌。建议将复杂逻辑提取到函数中,保持模板简洁 ✅。
如何输出原始 $
符号?
若要显示字面意义上的 $
而非触发模板替换,需进行转义:
val message = "n = \$n"
此时 $n
不再被识别为变量引用,而是普通文本。
4. 原始字符串(Raw Strings)
Kotlin 提供三重引号 """..."""
定义的原始字符串,适用于多行文本和含特殊字符的内容,无需转义反斜杠等符号。
多行文本示例
Java 中定义 Windows 路径需要双重转义:
String path = "C:\\Repository\\read.me";
Kotlin 使用原始字符串更清晰:
val path = """C:\Repository\read.me"""
多行字符串与缩进控制
可用于构建结构化文本,例如账单信息:
val receipt = """Item 1: $1.00
Item 2: $0.50"""
该字符串精确保留换行,生成两行内容。
若希望代码美观并统一缩进,可用 trimMargin()
:
val receipt = """Item 1: $1.00
>Item 2: $0.50""".trimMargin(">")
✅ trimMargin(">")
会移除每行开头到第一个 >
为止的所有空白字符,最终输出仍保持整洁。
原始字符串中的限制
❌ 原始字符串不支持任何转义序列。例如:
val receipt = """Item 1: $1.00\nItem 2: $0.50"""
结果不会换行,而是原样输出 \n
两个字符。
但支持模板功能!
尽管不能使用 \n
,但原始字符串依然支持字符串模板:
val receipt = """Item 1: $1.00${"\n"}Item 2: $0.50"""
👉 利用 ${}
插入真正的换行符 \n
,巧妙绕过限制。
5. 多行字符串中使用美元符号($)的解决方案
当你的字符串包含 $
且不属于 Kotlin 变量时(如 MongoDB 查询),很容易踩坑。
5.1 问题背景
假设我们在 Kotlin 应用中构造 MongoDB 查询语句,筛选年龄 ≥18 的用户:
db.people.find(
{
"age": { $gte: 18 }
}
)
尝试直接放入原始字符串:
val findAdultsQuery = """
db.people.find(
{
"age": { $gte: 18 }
}
)
"""
❌ 编译失败!Kotlin 试图将 $gte
解释为变量,但未定义。
尝试转义:
val findAdultsQuery = """
db.people.find(
{
"age": { \$gte: 18 }
}
)
"""
❌ 仍然报错!因为原始字符串 不支持反斜杠转义特殊字符。
5.2 推荐方案:使用 ${'$'}
插值
✅ 最佳实践是利用字符串模板插入字面量 $
:
val dollarChar = "$"
val findAdultsQuery = """
db.people.find(
{
"age": { ${'$'}gte: 18 }
}
)
"""
assertTrue(dollarChar in findAdultsQuery)
✔️ 成功生成包含 $gte
的正确 JSON 查询。
💡 原理:${'$'}
是一个表达式,返回字符 $
,然后作为字符串拼入。
此方法也适用于带 \n
的双引号多行字符串:
val dollarChar = "$"
val findAdultsQuery = "db.people.find(\n{\n\"age\": { ${'$'}gte: 18 }\n }\n)"
assertTrue(dollarChar in findAdultsQuery)
5.3 替代方案:使用双引号 + 转义
如果对格式要求不高,也可改用双引号字符串,并用 \
转义 $
:
val dollarChar = "$"
val findAdultsQuery = "db.people.find(\n{\n\"age\": { \$gte: 18 }\n }\n)"
assertTrue(dollarChar in findAdultsQuery)
✅ 此方式有效,但牺牲了可读性,尤其在复杂 JSON 场景下维护困难。
📌 总结建议:
- 日常优先使用
"""..."""
配合${'$'}
; - 简单场景可用双引号转义;
- 避免混合过多逻辑在字符串内,必要时封装成函数。
6. 总结
Kotlin 的字符串模板是一项强大且 Java 所不具备的功能,极大简化了字符串拼接和动态内容生成。
我们重点探讨了:
- 模板语法
$variable
和${expression}
- 原始字符串
"""..."""
的优势与限制 - 在 MongoDB 等 DSL 场景中安全使用
$
的最佳实践:${'$'}
所有示例代码均可在 GitHub 获取:https://github.com/Baeldung/kotlin-tutorials/tree/master/core-kotlin-modules/core-kotlin-strings