Scala基本数据类型(字面量)

2019-05-04 17:06:45 | 编辑 | 添加

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")

箭头操作符只适用于两元素的元组。