Scala 强制要求变量在声明时赋值。
var Pi : Double = 3.1415
Scala 的基本数据类型和 Java 一样分为八种:Short,Byte,Int,Long,Float,Double,Boolean,Char。
Scala 支持类型推断,对于未声明类型的变量,它的实际类型取决于第一次字面量赋值。比如,下面的变量 a 会被编译器翻译成 String 类型。
var a = "Hello Scala"
之后,变量 a 的数据类型将 保持不变 ,这符合 Scala 作为强类型语言的特性。在未声明类型的情况下,无浮点数值默认为 Int 类型,所有的浮点数默认是 Double 类型。可以通过补充后缀来显式地表明数据类型:
var v1 = 3.2f // Float
var v2 = 3.2 // Double
var v3 = 3 // Int
var v4 = 3l // long
无法推断类型的变量会被设置为 Any 类型。比如:
// Any
val a = if (Random.nextInt() % 2 == 0) {1} else {"hello"}
Scala 使用 var 表示一个 可变变量 ( variable ),使用 val 来表示一个 不变值 ( value ) 。只有 var 变量可以改变引用。不可变的值意味着它是线程安全的,不需要锁机制来保持同步。因为这个优点,在后续的函数式 / 并发编程中,优先使用值拷贝来传递状态,避免修改变量来改变状态。
var obj1 = new Object()
obj1 = new Object() // ok
val obj2 = new Object()
obj2 = new Object() // error
特别地,对于 val 类型的值,Scala 支持将其延迟加载。举一个例子:
val t1 = System.currentTimeMillis()
val expensive = {Thread.sleep(1000);"hello"}
val duration = System.currentTimeMillis() - t1
println(s"duration: ${duration}ms")
由于 expensive 的赋值块内部包含了一条休眠命令,因此程序对其进行初始化时需要停顿将近 1 秒的时间,即便 expensive 在后面的程序中并没有用到。这种情况下,对 expensive 进行初始化是一种资源的浪费。在实际场合,如果不确定一个变量是否会被使用,且初始化代价又十分高昂时,那么不妨使用 lazy 关键字令它被延迟加载。
// 它现在只会在第一次被调用时加载。
lazy val expensive = {Thread.sleep(1000);"hello"}
不过,为了表述方便,下文 var 和 val 统称为变量。
数据类型
Scala 的数据类型分为 AnyVal ( 值类型 ) 以及 AnyRef ( 引用类型 ) 。一切 AnyVal 都是对象,包括 Int,Double,Float 等,因此 Scala 的数值天生就携带各种 toXXX 方法用于转换类型。下图为 Scala 数据类型族谱:
Any 是所有数据类型的顶类型,Nothing 则是所有类型的底类型。Null 类型有且只有一个值:null。它是所有 AnyRef 类的底类型,这样设计的目的是可以将 null 赋值给任何一个 AnyRef 类型。
Unit 被归为 AnyVal 的子类型。它用于表示函数没有返回值,比如主程序的返回值就是 Unit 类型。它有且只有一个值,为 ()。
浮点型数据有多种表示方法:
-
十进制表示方式:
5.12,5.12f,.512( 表示0.512) -
科学计数法方式:
5.12e2( 5.12 × 102 ),5.12e-2( 5.12 × 10-2 )
下表给出了不同数据类型的值域:
| type | 描述 |
|---|---|
Byte | 8 位 ( 1 字节 ) 有符号补码整数。区间 -128 ~ 127。 |
Short | 16 位 ( 2 字节 ) 有符号补码整数。区间 -(2^16) ~ (2^16)-1 |
Int | 32 位 ( 4 字节 ) 有符号补码整数。区间 -(2^32) ~ (2^32)-1 |
Long | 64 位 ( 8 字节 ) 有符号补码整数。区间 -(2^64) ~ (2^64)-1 |
Float | 32 位,IEEE 754 标准的单精度浮点数。 |
Double | 64 位,IEEE 754 标准的单精度浮点数。 |
Char | 16 位无符号的 Unicode 字符,区间值为 U+0000 ~ U+FFFF。 |
String | 字符序列。 |
Boolean | 只有两个值:true 和 false。 |
Unit | 只有唯一值 (),常用于表示方法 / 函数无返回值,相当于 Java 的 void。 |
Null | 只有唯一值 null,表示空的对象。 |
Nothing | 是所有 Scala 类型的底类型,常用于抛出异常。 |
Any | 是所有 Scala 类型的顶类型。 |
AnyRef | 是 Scala 一切引用类型的父类型。 |
值转换
低精度的值可以赋值给高精度类型的变量或值,该过程是自动转换。反之则需要牺牲精度,该过程是强制转换。
在多种类型的数据混合计算时,系统的计算结果取 精度最大的数据类型。比如,Int 和 Float 的数值运算是 Float 类型,Int 和 Long 的数值计算是 Long 类型。
val a : Int = 100
val b : Long = 200l
val c = a + b // c : Long
低精度的变量不能接受高精度的值。
val a : Float = 10.d // error
Char 和 Int 两个数据类型之间可以自动转换:
- 为一个
Char类型变量 / 值赋予一个Int值时,结果是Unicode编码中对应该数值编号的字符; - 为一个
Int类型变量赋予一个Char值时,结果是字符在Unicode编码中的数值编号。
在计算 'a' + 1 时,程序首先会将 Char 类型的 'a' 转换为 Int 数值,因此计算结果也是 Int 类型。注意,表达式不会执行自动转换。比如下方的代码:
val a: Char = 66 // ok
val b: Char = 65 + 1 // error
可以在计算的结果上通过 toChar 方法显式转换。
val char1 : Char = (65 + 1).toChar
Byte、Short 这两种数据类型不会自动转换到 Char,需要使用toChar 方法进行强制转换。
val bt : Byte = 123
val ch : Char = bt.toChar
Byte,Short,Char 之间相互计算的结果是 Int 类型。
val a : Byte = 20 // -128 ~ 127
val b : Char = 'a' // 97
val c : Short = 200 // -2^16 ~ 2^16-1
val d : Int = a + b + c
println(d)
低精度类型的变量接受高精度值时需要以牺牲精度的代价进行 强制转换。比如:Float 是比 Int 精度更高的类型,因此将 Float 值赋给 Int 变量时必须进行强制转换。
val a : Int = 1.4f.toInt
一切 AnyVal 类型都可以调用 toString 方法转换为 String 类型;String 类型也可以通过 toXXX 方法转换成值类型。如:
val a : Int = "100".toInt // ok
val b : String = 100.toString // ok
val c : Double = "hello".toDouble // error