所有值都有类型
在 Scala 中,所有值都有类型,包括数值和函数。下图展示了类型层次结构的一个子集。
Scala 类型层次结构
Any 是所有类型的超类型,也称为顶级类型。它定义了一些通用方法,如 equals、hashCode 和 toString。
顶级类型 Any 有一个子类型 Matchable,用于标记我们可以对其执行模式匹配的所有类型。保证称为“参数化”的属性非常重要。我们在此不展开细节,但总结来说,这意味着我们无法对类型为 Any 的值执行模式匹配,而只能对 Matchable 的子类型的值执行模式匹配。参考文档包含有关 Matchable 的更多信息。
Matchable 有两个重要的子类型:AnyVal 和 AnyRef。
AnyVal 表示值类型。有一些预定义的值类型,它们是不可空的:Double、Float、Long、Int、Short、Byte、Char、Unit 和 Boolean。 Unit 是不携带任何有意义的信息的值类型。Unit 只有一个实例,我们可以将其称为:()。
AnyRef 表示引用类型。所有非值类型都被定义为引用类型。Scala 中的每个用户定义类型都是 AnyRef 的子类型。如果在 Java 运行时环境的上下文中使用 Scala,AnyRef 对应于 java.lang.Object。
在基于语句的语言中,void 用于不返回任何内容的方法。如果你在 Scala 中编写没有返回值的方法,例如以下方法,Unit 用于相同目的
def printIt(a: Any): Unit = println(a)
以下是一个示例,演示字符串、整数、字符、布尔值和函数都是 Any 的实例,并且可以像其他任何对象一样处理
val list: List[Any] = List(
"a string",
732, // an integer
'c', // a character
'\'', // a character with a backslash escape
true, // a boolean value
() => "an anonymous function returning a string"
)
list.foreach(element => println(element))
该代码定义了一个类型为 List[Any] 的值 list。该列表使用各种类型的元素进行初始化,但每个元素都是 scala.Any 的一个实例,因此我们可以将它们添加到列表中。
以下是该程序的输出
a string
732
c
'
true
<function>
Scala 的“值类型”
如上所示,Scala 的数字类型扩展了 AnyVal,它们都是完全成熟的对象。这些示例展示了如何声明这些数字类型的变量
val b: Byte = 1
val i: Int = 1
val l: Long = 1
val s: Short = 1
val d: Double = 2.0
val f: Float = 3.0
在前四个示例中,如果你没有明确指定类型,数字 1 将默认为 Int,因此如果你想要其他数据类型(Byte、Long 或 Short),你需要明确声明这些类型,如所示。带有小数点(例如 2.0)的数字将默认为 Double,因此如果你想要 Float,你需要声明 Float,如最后一个示例所示。
由于 Int 和 Double 是默认数字类型,因此你通常在不显式声明数据类型的情况下创建它们
val i = 123 // defaults to Int
val x = 1.0 // defaults to Double
在代码中,您还可以将字符 L、D 和 F(及其小写等效形式)附加到数字,以指定它们是 Long、Double 或 Float 值
val x = 1_000L // val x: Long = 1000
val y = 2.2D // val y: Double = 2.2
val z = -3.3F // val z: Float = -3.3
您还可以使用十六进制表示法来设置整数格式(通常为 Int,但它也支持 L 后缀,以指定它们是 Long)
val a = 0xACE // val a: Int = 2766
val b = 0xfd_3aL // val b: Long = 64826
Scala 支持多种不同的方式来设置同一浮点数的格式,例如
val q = .25 // val q: Double = 0.25
val r = 2.5e-1 // val r: Double = 0.25
val s = .0025e2F // val s: Float = 0.25
Scala 还具有 String 和 Char 类型,您通常可以使用隐式形式声明它们
val s = "Bill"
val c = 'a'
如所示,将字符串用双引号括起来,或将多行字符串用三引号括起来,并将字符用单引号括起来。
这些数据类型及其范围为
| 数据类型 | 可能值 |
|---|---|
| 布尔型 | true 或 false |
| 字节 | 8 位带符号二进制补码整数(-2^7 至 2^7-1,包括) -128 至 127 |
| 短整型 | 16 位带符号二进制补码整数(-2^15 至 2^15-1,包括) -32,768 至 32,767 |
| 整型 | 32 位二进制补码整数(-2^31 至 2^31-1,包括) -2,147,483,648 至 2,147,483,647 |
| 长整型 | 64 位二进制补码整数(-2^63 至 2^63-1,包括) (-2^63 至 2^63-1,包括) |
| 浮点型 | 32 位 IEEE 754 单精度浮点数 1.40129846432481707e-45 至 3.40282346638528860e+38 |
| 双精度浮点型 | 64 位 IEEE 754 双精度浮点数 4.94065645841246544e-324 至 1.79769313486231570e+308 |
| 字符型 | 16 位无符号 Unicode 字符(0 至 2^16-1,包括) 0 至 65,535 |
| 字符串 | Char 的序列 |
字符串
Scala 字符串类似于 Java 字符串,但与 Java 不同(至少在 Java 15 之前),使用三引号很容易创建多行字符串
val quote = """The essence of Scala:
Fusion of functional and object-oriented
programming in a typed setting."""
这种基本方法的一个缺点是,第一行之后的行都是缩进的,并且看起来像这样
"The essence of Scala:
Fusion of functional and object-oriented
programming in a typed setting."
当间距很重要时,在第一行之后的每一行前面放置一个 | 符号,并在字符串之后调用 stripMargin 方法
val quote = """The essence of Scala:
|Fusion of functional and object-oriented
|programming in a typed setting.""".stripMargin
现在,所有行都在字符串内左对齐
"The essence of Scala:
Fusion of functional and object-oriented
programming in a typed setting."
Scala 字符串还支持强大的字符串插值方法,我们将在 下一章 中讨论这些方法。
BigInt 和 BigDecimal
当您需要非常大的数字时,请使用 BigInt 和 BigDecimal 类型
val a = BigInt(1_234_567_890_987_654_321L)
val b = BigDecimal(123_456.789)
其中 Double 和 Float 是近似十进制数,而 BigDecimal 用于精确算术,例如处理货币时。
BigInt 和 BigDecimal 的一个优点是,它们支持您习惯于与数字类型一起使用的所有运算符
val b = BigInt(1234567890) // scala.math.BigInt = 1234567890
val c = b + b // scala.math.BigInt = 2469135780
val d = b * b // scala.math.BigInt = 1524157875019052100
类型转换
例如
val b: Byte = 127
val i: Int = b // 127
val face: Char = '☺'
val number: Int = face // 9786
只有在不丢失信息的情况下,您才能转换为某个类型。否则,您需要明确转换
val x: Long = 987654321
val y: Float = x.toFloat // 9.8765434E8 (note that `.toFloat` is required because the cast results in precision loss)
val z: Long = y // Error
您还可以将引用类型转换为子类型。这将在本教程的后面部分介绍。
Nothing 和 null
Nothing 是所有类型的子类型,也称为底部类型。没有类型为 Nothing 的值。一个常见的用途是表示非终止,例如抛出的异常、程序退出或无限循环——即,它是一个表达式类型的类型,该表达式不会求值为一个值,或一个不会正常返回的方法。
Null 是所有引用类型(即 AnyRef 的任何子类型)的子类型。它有一个由关键字文字 null 标识的单个值。目前,使用 null 被认为是不良做法。它应该主要用于与其他 JVM 语言的互操作性。一个选择加入的编译器选项将 Null 的状态更改为修复与其用法相关的注意事项。此选项可能会在 Scala 的未来版本中成为默认选项。您可以在 此处 了解更多信息。
在此期间,在 Scala 代码中几乎永远不应该使用 null。本书的 函数式编程章节 和 API 文档 中讨论了 null 的替代方法。