所有值都有类型
在 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
的替代方法。