所有值都有一个类型
在 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
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
Scala还具有 String
和 Char
类型,通常可以使用隐式形式声明:
val s = "Bill"
val c = 'a'
如下面表格所示,将字符串括在双引号中 — 或多行字符串括在三引号中 — 或字符括在单引号中。
这些数据类型及其范围包括:
数据类型 | 可能的值 |
---|---|
Boolean | true 或 false |
Byte | 8 位有符号二进制补码整数(-2^7 至 2^7-1,含) -128 至 127 |
Short | 16 位有符号二进制补码整数(-2^15 至 2^15-1,含) -32,768 至 32,767 |
Int | 32 位二进制补码整数(-2^31 至 2^31-1,含) -2,147,483,648 至 2,147,483,647 |
Long | 64 位 2 的补码整数(-2^63 到 2^63-1,含) (-2^63 到 2^63-1,包括) |
Float | 32 位 IEEE 754 单精度浮点数 1.40129846432481707e-45 到 3.40282346638528860e+38 |
Double | 64 位 IEEE 754 双精度浮点数 4.94065645841246544e-324 到 1.79769313486231570e+308 |
Char | 16 位无符号 Unicode 字符(0 到 2^16-1,含) 0 到 65,535 |
String | 一个 Char 序列 |
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
关于字符串的两个注释
Scala字符串类似于Java字符串,但它们有两个很棒的附加特性:
- 它们支持字符串插值
- 创建多行字符串很容易
字符串插值
字符串插值提供了一种非常可读的方式在字符串中使用变量。 例如,给定以下三个变量:
val firstName = "John"
val mi = 'C'
val lastName = "Doe"
你可以把那些变量组合成这样的字符串:
println(s"Name: $firstName $mi $lastName") // "Name: John C Doe"
只需在字符串前面加上字母 s
,然后在字符串内的变量名称之前放置一个 $
符号。
如果要在字符串中使用可能较大的表达式时,请将它们放在大括号中:
println(s"2 + 2 = ${2 + 2}") // prints "2 + 2 = 4"
val x = -1
println(s"x.abs = ${x.abs}") // prints "x.abs = 1"
其他插值器
放置在字符串前面的 s
只是一个可能的插值器。
如果使用 f
而不是 s
,则可以在字符串中使用 printf
样式的格式语法。
此外,字符串插值器只是一种特殊方法,可以定义自己的方法。
例如,一些数据库库定义了非常强大的 sql
插值器。
多行字符串
多行字符串是通过将字符串包含在三个双引号内来创建的:
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."
类型转换
可以通过以下方式强制转换值类型:
例如:
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 (注意 `.toFloat` 是必须的,因为强制类型转换后的精度会损)
val z: Long = y // Error
还可以将引用类型强制转换为子类型。 这将在教程的后面部分介绍。
Nothing
和 null
Nothing
是所有类型的子类型,也称为底部类型。
Nothing
类型是没有值的。
一个常见的用途是发出非终止信号,例如抛出异常,程序退出或无限循环—即,它是不计算为值的那种表达式,或者不正常返回的方法。
Null
是所有引用类型的子类型(即 AnyRef
的任何子类型)。
它具有由关键字面量 null
标识的单个值。
目前,使用 null
被认为是不好的做法。它应该主要用于与其他JVM语言的互操作性。选择加入编译器选项会更改 Null
状态,以修复与其用法相关的警告。此选项可能会成为将来版本的 Scala 中的默认值。你可以在这里了解更多关于它的信息。
与此同时, null
几乎不应该在Scala代码中使用。
本书的函数式编程章节和 API文档中讨论了 null
的替代方法。
Contributors to this page:
Contents
- 导言
- Scala 3 特性
- 为什么是 Scala 3 ?
- Scala 的味道
- Hello, World!
- The REPL
- 变量和数据类型
- 控制结构
- 领域建模
- 方法
- 头等函数
- 单例对象
- 集合
- 上下文抽象
- 顶层定义
- 总结
- 类型初探
- 字符串插值
- 控制结构
- 领域建模
- 工具
- OOP 领域建模
- 函数式领域建模
- 方法
- 方法特性
- main 方法
- 总结
- 函数
- 匿名函数
- 函数变量
- Eta 扩展
- 高阶函数
- 自定义 map 函数
- 创建可以返回函数的方法
- 总结
- 打包和导入
- Scala 集合
- 集合类型
- 集合方法
- 总结
- 函数式编程
- 什么是函数式编程?
- 不可变值
- 纯函数
- 函数是值
- 函数式错误处理
- 总结
- 类型和类型系统
- 类型推断
- 泛型
- 相交类型
- 联合类型
- 代数数据类型
- 型变
- 不透明类型
- 结构化类型
- 依赖函数类型
- 其他类型
- 上下文抽象
- 扩展方法
- Given 实例和 Using 语句
- 上下文绑定
- Given 导入
- 实现类型类
- 多元相等性
- 隐式转换
- 总结
- 并发
- Scala 工具
- 使用 sbt 构建和测试 Scala 项目
- worksheet
- 与 Java 交互
- 向 Java 开发者介绍Scala
- Scala for JavaScript Developers
- Scala for Python Developers
- 下一步去哪