Scala 3 — 书籍

类型初探

语言

所有值都有类型

在 Scala 中,所有值都有类型,包括数值和函数。下图展示了类型层次结构的一个子集。

Scala 3 Type Hierarchy

Scala 类型层次结构

Any 是所有类型的超类型,也称为顶级类型。它定义了一些通用方法,如 equalshashCodetoString

顶级类型 Any 有一个子类型 Matchable,用于标记我们可以对其执行模式匹配的所有类型。保证称为“参数化”的属性非常重要。我们在此不展开细节,但总结来说,这意味着我们无法对类型为 Any 的值执行模式匹配,而只能对 Matchable 的子类型的值执行模式匹配。参考文档包含有关 Matchable 的更多信息。

Matchable 有两个重要的子类型:AnyValAnyRef

AnyVal 表示值类型。有一些预定义的值类型,它们是不可空的:DoubleFloatLongIntShortByteCharUnitBooleanUnit 是不携带任何有意义的信息的值类型。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,因此如果你想要其他数据类型(ByteLongShort),你需要明确声明这些类型,如所示。带有小数点(例如 2.0)的数字将默认为 Double,因此如果你想要 Float,你需要声明 Float,如最后一个示例所示。

由于 IntDouble 是默认数字类型,因此你通常在不显式声明数据类型的情况下创建它们

val i = 123   // defaults to Int
val x = 1.0   // defaults to Double

在代码中,您还可以将字符 LDF(及其小写等效形式)附加到数字,以指定它们是 LongDoubleFloat

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 还具有 StringChar 类型,您通常可以使用隐式形式声明它们

val s = "Bill"
val c = 'a'

如所示,将字符串用双引号括起来,或将多行字符串用三引号括起来,并将字符用单引号括起来。

这些数据类型及其范围为

数据类型 可能值
布尔型 truefalse
字节 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 字符串还支持强大的字符串插值方法,我们将在 下一章 中讨论这些方法。

BigIntBigDecimal

当您需要非常大的数字时,请使用 BigIntBigDecimal 类型

val a = BigInt(1_234_567_890_987_654_321L)
val b = BigDecimal(123_456.789)

其中 DoubleFloat 是近似十进制数,而 BigDecimal 用于精确算术,例如处理货币时。

BigIntBigDecimal 的一个优点是,它们支持您习惯于与数字类型一起使用的所有运算符

val b = BigInt(1234567890)   // scala.math.BigInt = 1234567890
val c = b + b                // scala.math.BigInt = 2469135780
val d = b * b                // scala.math.BigInt = 1524157875019052100

类型转换

值类型可以用以下方式转换: Scala 类型层次结构

例如

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

您还可以将引用类型转换为子类型。这将在本教程的后面部分介绍。

Nothingnull

Nothing 是所有类型的子类型,也称为底部类型。没有类型为 Nothing 的值。一个常见的用途是表示非终止,例如抛出的异常、程序退出或无限循环——即,它是一个表达式类型的类型,该表达式不会求值为一个值,或一个不会正常返回的方法。

Null 是所有引用类型(即 AnyRef 的任何子类型)的子类型。它有一个由关键字文字 null 标识的单个值。目前,使用 null 被认为是不良做法。它应该主要用于与其他 JVM 语言的互操作性。一个选择加入的编译器选项将 Null 的状态更改为修复与其用法相关的注意事项。此选项可能会在 Scala 的未来版本中成为默认选项。您可以在 此处 了解更多信息。

在此期间,在 Scala 代码中几乎永远不应该使用 null。本书的 函数式编程章节API 文档 中讨论了 null 的替代方法。

此页面的贡献者