在写 Swift 的时候,遇到了一个看似平平无奇、但细想却有点“怪”的代码:
1 2 var char: Character = "A" var str: String = "A"
第一眼看上去没什么问题——一个是 Character,一个是 String,右边都是 “A”。
但问题来了: 👉 为什么同一个 “A”,既可以赋值给 Character,也可以赋值给 String?
这背后其实牵扯到 Swift 一个非常核心但又经常被忽略的机制:字面量协议(Literal Protocols)。
一、直觉 vs 实际 如果用传统语言(比如 C / Java)的思维:
但 Swift 没有 ‘A’ 这种语法,只有 “A”。于是问题就变成: 👉 “A” 到底是 String,还是 Character?
答案是:
它一开始什么都不是。
二、字面量其实是“无类型”的 在 Swift 中:"A"并不是一个 String,而是一个字面量(literal)。 它的类型由上下文决定:
1 2 let a: String = "A" let b: Character = "A"
也就是说: 👉 “A” 是一个“可被解释”的值,而不是固定类型的对象。
三、字面量协议 Swift 通过一整套协议,让不同类型可以“接收”字面量。 你可以把它理解成: 字面量 = 编译器帮你调用某个 init(...)
字符串
ExpressibleByStringLiteral
ExpressibleByExtendedGraphemeClusterLiteral
ExpressibleByUnicodeScalarLiteral 这三个协议分别对应不同层次的字符串:
String 遵循 ExpressibleByStringLiteral
Character 遵循 ExpressibleByExtendedGraphemeClusterLiteral
UnicodeScalar 遵循 ExpressibleByUnicodeScalarLiteral
这三个的区别在于:
String 是一串字符的集合;
Character 是一个“扩展字形簇”(可能由多个 Unicode 标量组成),比如 emoji 表情和中文字符;
UnicodeScalar 是一个单一的 Unicode 标量。
示例:
1 2 3 let str: String = "A" let char: Character = "A" let scalar: UnicodeScalar = "A"
⚠️ 关键点 同一个字面量:"A",可以根据上下文走不同协议。
字符串插值 ExpressibleByStringInterpolation 协议让你可以在字符串字面量中插入变量和表达式: 支持这种语法:
1 2 let name = "Swift" let s = "Hello \(name) "
你甚至可以自定义解析规则,实现 DSL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 struct Log : ExpressibleByStringInterpolation { init (stringLiteral value : String ) {} init (stringInterpolation : StringInterpolation ) {} struct StringInterpolation : StringInterpolationProtocol { init (literalCapacity : Int , interpolationCount : Int ) {} mutating func appendLiteral (_ literal : String ) {} mutating func appendInterpolation (_ value : Int ) { print ("int:" , value) } } }let log: Log = "Value is \(42 ) "
同样的,比如 SQL DSL 也可以通过字符串插值来实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 struct SQL : ExpressibleByStringInterpolation { init (stringLiteral value : String ) {} init (stringInterpolation : StringInterpolation ) {} struct StringInterpolation : StringInterpolationProtocol { init (literalCapacity : Int , interpolationCount : Int ) {} mutating func appendLiteral (_ literal : String ) {} mutating func appendInterpolation (_ value : Int ) { print ("value:" , value) } } }let query: SQL = "SELECT * FROM users WHERE id = \(42 ) "
整数、浮点数 同样的道理,整数和浮点数也有对应的协议:
ExpressibleByIntegerLiteral
ExpressibleByFloatLiteral
示例:
1 2 3 4 5 6 struct MyInt : ExpressibleByIntegerLiteral { init (integerLiteral value : Int ) { print ("init with" , value) } }let x: MyInt = 42
Bool ExpressibleByBooleanLiteral 协议,让你可以用 true 和 false 来初始化自定义类型:
1 2 3 4 5 6 7 struct MyBool : ExpressibleByBooleanLiteral { init (booleanLiteral value : Bool ) { print (value) } }let b: MyBool = true
数组 ExpressibleByArrayLiteral 协议,让你可以用数组字面量来初始化自定义类型:
1 2 3 4 5 6 7 struct MyArray : ExpressibleByArrayLiteral { init (arrayLiteral elements : Int ...) { print (elements) } }let arr: MyArray = [1 , 2 , 3 ]
字典 ExpressibleByDictionaryLiteral 协议,让你可以用字典字面量来初始化自定义类型:
1 2 3 4 5 6 7 struct MyDict : ExpressibleByDictionaryLiteral { init (dictionaryLiteral elements : (String , Int )... ) { print (elements) } }let dict: MyDict = ["a" : 1 , "b" : 2 ]
nil ExpressibleByNilLiteral 协议,让你可以用 nil 来初始化自定义类型:
1 2 3 4 5 6 7 8 ExpressibleByNilLiteral struct MyOptional : ExpressibleByNilLiteral { init (nilLiteral : ()) { print ("nil" ) } }let x: MyOptional = nil
总结 很多人会误以为这是“自动类型转换”:Character("A") 但其实: ❌ 不是运行时转换 ✅ 是编译期选择构造方式
1 2 3 let a = 10 let b = "hello" let c = [1 , 2 , 3 ]
👉 看起来是“语言内建”,实际上全部由协议驱动。 👉 Swift 的字面量,不是值,而是“构造语法”。