val book = "Programming Scala"。在这行代码中,我们用一个String 字面量初始化了一个val 变量book。还有(s: String) =>s.toUpperCase,这也是一个函数字面量的例子。Scala的基本数据类型和Java的基本数据类型还是有很大区别的,Scala的字面量只有再编译时才会成为字面量,我们现在来讨论Scala 支持的所有字面量。
1 整数字面量
整数字面量可以以十进制、十六进制或八进制的形式出现。表2-2 给出了整数字面量的详细信息。
类 型 | 格 式 | 例 子 |
十进制 | 0 或一个非零值,后面跟上0 个或多个数字(0-9) | 0,1,321 |
十六进制 | 0x 后面跟上一个或多个十六进制数字(0-9,A-F,a-f) | 0xFF,0x1a3b |
八进制 | 0 后面跟上一个多个八进制数字(0-7),八进制数字字面量在Scala 2.10 中已经废弃。 | 13,077 |
在数字字面量之前加上- 符号,可以表示负数。
对于Long 类型的字面量,除非你将该字面量赋值给一个Long 类型的变量,否则需要在数字字面量后面加上L 或l。如若不加,字面量的类型将默认推断为Int。整数字面量的有效值范围是根据被赋值的变量类型决定的。下面列出了各类型的上下限(包含上下限本身)。
整数字面量的取值范围(包含边界)
目标类型 | 下限(包含) | 上限(包含) |
Long | -263 | 263-1 |
Int | -231 | 231-1 |
Short | -215 | 215-1 |
Char | 0 | 216-1 |
Byte | -27 | 27-1 |
如果整数字面量的值超出了以上表格中所示的范围,将会引发一个编译错误。如下面这个例子:
scala> val i = 1234567890123 <console>:1: error: integer number too large val i = 12345678901234567890 scala> val i = 1234567890123L i: Long = 1234567890123 scala> val b: Byte = 128 <console>:19: error: type mismatch; found : Int(128) required: Byte val b: Byte = 128 scala> val b: Byte = 127 b: Byte = 127
2 浮点数字面量
浮点数字面量的形式为:首先是一个可省略的负号,然后是0 个或多个数字后面跟上一个点号(.),后面再跟上一个或多个数字。对于Float 类型的字面量,还要在后面加上F 或f,否则会被认为是Double 类型。你也可以在后面加上D 或d,明确表示字面量为Double字面量。浮点数字面量可以用指数表示。指数部分的形式为E 或e 后面跟上可选的+ 或-,然后是一个或多个数字。
以下是浮点数字面量的一些例子。例子中除非被赋值的变量是Float 类型,或者在字面量后面加了F 或f,否则字面量都被推断为Double 类型:
.14 3.14 3.14f 3.14F 3.14d 3.14D 3e5 3E5 3.14e+5 3.14e-5 3.14e-5 3.14e-5f 3.14e-5F 3.14e-5d 3.14e-5D
Float 遵循IEEE 754 32 位单精度浮点数规范。
Double 遵循IEEE 754 64 位双精度浮点数规范。
在Scala 2.10 之前,小数点后没有数字是允许的,如3. 和3.e5。但是由于这种语法会导致歧义:小数点可能被解释为方法名前的句号,因此1.toString 应该如何解析?是Int 类型的1,还是Double 类型的1.0 ?所以,小数点后不带数字的浮点数字面量在Scala 2.10 中就被废弃,而在Scala 2.11 中则不被允许。
3 布尔型字面量
布尔型字面量可以为true 或false。被这两个字面量赋值的变量,其类型将被推断为
Boolean: scala> val b1 = true b1: Boolean = true scala> val b2 = false b2: Boolean = false
4 字符字面量
字符字面量要么是单引号内的一个可打印的Unicode 字符,要么是一个转义序列。值在0~255 的Unicode 字符也可以用八进制数字的转义形式表示,即一个反斜杠(\)后面跟上最多3 个八进制数字字符。如果反斜杠后面不是有效的转义序列,会引发编译时的错误。
以下是字符字面量的例子:
'A' '\u0041' // Unicode中的'A' '\n' '\012' // 八进制的'\n' '\t'
字符的转义序列
转义序列 | 含 义 |
\b | 退格(BS) |
\t | 水平制表符(HT) |
\n | 换行(LF) |
\f | 表格换行(FF) |
\r | 回车(CR) |
\" | 双引号(") |
\’ | 单引号(’) |
\\ | 反斜杠(\) |
注意,不可打印的Unicode 字符,如\u0009(水平制表符)是不允许的。应该使用等价的转义形式\t。回顾一下表2-1 中提到的3 个Unicode 字符,可以有效地替换相应的ASCII序列:? 替换=>,→替换->,←替换<-。
5 字符串字面量
字符串字面量是被双引号或者三重双引号包围的字符串序列,如"""…"""。
对于双引号包围的字符串字面量,允许出现的字符与字符字面量相同。但是,如果双引号字符出现在字符串中,则必须使用反斜杠\ 进行转义。以下是字符串字面量的例子:
"Programming\nScala" "He exclaimed, \"Scala is great!\"" "First\tSecond"
用三重双引号包含的字符串字面量也被称为多行字符串字面量。这些字符串可以跨越多行,换行符是字符串的一部分。可以包含任意字符,可以是一个双引号也可以是两个连续的双引号,但不允许出现三个连续的双引号。对于包含有反斜杠\,但反斜杠不用于构成Unicode 字符,也不用于构成有效转义序列的字符串很适合采用这种字符串字面量。正则表达式就是一个很好的例子,在正则表达式中经常用转义的字符表示特殊含义。即使转义序列出现,三重双引号包含的字符串也不对其进行转义。
以下给出了3 个示例字符串:
"""Programming\nScala""" """He exclaimed, "Scala is great!" """ """First line\n Second line\t Fourth line"""
注意,在第二个例子中,必须在结尾的""" 前加一个空格,以防止出现解析错误。试图将"Scala is great!" 的第二个双引号进行转义("Scala is great!\")的行为是无效的。
使用多行字符串时,你可能希望各行子串有良好的缩进以使代码美观,但却不希望多余的空格出现在字符串的输出中。String.stripMargin 可以解决这个问题。它会移除每行字符串开头的空格和第一个遇到的垂直分隔符|。如果你需要自行添加空格制造缩进,你应该在| 后添加你要的空格。参照以下示例:
// src/main/scala/progscala2/typelessdomore/multiline-strings.sc def hello(name: String) = s"""Welcome! Hello, $name! * (Gratuitous Star!!) |We're glad you're here. | Have some extra whitespace.""".stripMargin hello("Programming Scala")
以上代码输出如下:
Welcome! Hello, Programming Scala! * (Gratuitous Star!!) We're glad you're here. Have some extra whitespace.
注意哪些行开头的空格被移除,而哪些行开头的空格未被移除。
如果你希望用别的字符代替|,可以用stripMargin 的重载版本,该函数可以指定一个Char 参数代替|。如果你想要移除整个字符串(而不是字符串的各个行)的前缀和后缀,有相应的stripPrefix 和stripSuffix 方法可以完成:
// src/main/scala/progscala2/typelessdomore/multiline-strings2.sc def goodbye(name: String) = s"""xxxGoodbye, ${name}yyy xxxCome again!yyy""".stripPrefix("xxx").stripSuffix("yyy") goodbye("Programming Scala")
上述例子的输出为:
Goodbye, Programming Scalayyy xxxCome again!
6 符号字面量
Scala 支持符号,符号是一些规定的字符串。两个同名的符号会指向内存中的同一对象。相比其他编程语言如Ruby 和Lisp,符号在Scala 中用得比较少。符号字面量是单引号(')后跟上一个或多个数字、字母或下划线(“_”),但第一个字符不能为数字。所以'1symbol 是无效的符号。符号字面量'id 是表达式scala.Symbol("id") 的简写形式,如果你需要创建一个包含空格的符号,可以使用Symbol.apply,Symbol(" Programming Scala ") 中的空格均为符号的一部分。
7 函数字面量
我们之前已经接触过函数字面量,但这里重述一下:(i: Int, s: String) => s+i 是一个类型为Function2[Int,String,String](返回值类型为String)的函数字面量。
甚至可以用函数字面量来声明变量,以下两种声明是等价的:
val f1: (Int,String) => String = (i, s) => s+i val f2: Function2[Int,String,String] = (i, s) => s+i
8 元组字面量
你希望从方法中返回多少次值(两个或多个值)?在很多语言中,如Java,你只有少数几种解决方案,且每一种都不是上选。比如,你可以传入参数,然后在方法中修改该参数,相当于“返回值”,但这样的代码很丑陋,不美观。(有的语言甚至用关键字来表示哪些参数是输入参数,哪些参数是输出参数。)你可以声明一个像“结构体”一样的类,类中有两个或多个值,然后返回该类的实例。
Scala 库中包含TupleN 类(如Tuple2),用于组建N 元素组,它以小括号加上逗号分隔的元素序列的形式来创建元素组。TupleN 表示的多个类各自独立,N 的取值从1 到22,包括22(在Scala 的未来版本中这个上限可能最终取消)。
例如,val tup = ("Programming Scala", 2014) 定义了一个Tuple2 的实例,实例中的第一个成员类型为String,从"Programming Scala" 中推断得到,第二个成员类型为Int,从2014 中推断得到。元组的实例是不可变的、first-class 的值(因为它们是对象,与你定义的其他类的实例没有区别),所以你可以将它们赋值给变量,将它们作为输入参数,或从方法中将其返回。
你也可以用字面量语法来声明Tuple 类型的变量:
val t1: (Int,String) = (1, "two") val t2: Tuple2[Int,String] = (1, "two")
以下例子演示了元组的用法:
val t = ("Hello", 1, 2.3) // 用字面量语法构造一个三个参数的元组Tuple3。 println( "Print the whole tuple: " + t ) println( "Print the first item: " + t._1 ) // 从元组中提取第一个元素(计数从1 开始,不从0 开始),接下来的2 行代码分别提取第二个和第三个元素。 println( "Print the second item: " + t._2 ) println( "Print the third item: " + t._3 ) val (t1, t2, t3) = ("World", '!', 0x22) // 声明了三个变量,t1、t2、t3,用元组中三个对应的元素对其赋值。 println( t1 + ", " + t2 + ", " + t3 ) val (t4, t5, t6) = Tuple3("World", '!', 0x22) // 用Tuple3 的“工厂”方法构造一个元组。 println( t4 + ", " + t5 + ", " + t6 )
运行该脚本,得到以下输出:
Print the whole tuple: (Hello,1,2.3) Print the first item: Hello Print the second item: 1 Print the third item: 2.3 World, !, 34 World, !, 34
表达式t._n 提取元组t 中的第n 个元素。为遵循历史惯例,这里从1 开始计数,而不是从0 开始。
一个两元素的元组,有时也被简称为pair。有很多定义pair 的方法,除了在圆括号中列出元素值以外,还可以把“箭头操作符”放在两个值之间,也可以用相应类的工厂方法:
(1, "one") 1 -> "one" 1 → "one" // 用 → 代替 -> Tuple2(1, "one")
箭头操作符只适用于两元素的元组。