此文档页面特定于 Scala 3,可能涵盖 Scala 2 中不可用的新概念。除非另有说明,本页面中的所有代码示例均假定你使用的是 Scala 3。
Scala 2 具有基于 Java 反射的较弱形式的结构类型,可通过 import scala.language.reflectiveCalls
实现.
简介
某些用例(例如建模数据库访问)在静态类型语言中比在动态类型语言中更尴尬。对于动态类型语言,将行建模为记录或对象并使用简单的点符号选择条目是很自然的,例如 row.columnName
。
要在静态类型语言中实现相同的体验,需要为数据库操作产生的每一行(包括连接和投影产生的行)定义一个类,并设置一个在行和表示它的类之间进行映射的方案。
这需要大量的样板代码,这导致开发人员为了更简单的方案(其中列名表示为字符串并传递给其他运算符,例如 row.select("columnName")
)而放弃静态类型的优势。这种方法放弃了静态类型的优势,并且仍然不如动态类型版本自然。
结构类型在您希望在动态上下文中支持简单的点表示法而又不丧失静态类型的优势时提供帮助。它们允许开发人员使用点表示法并配置如何解析字段和方法。
示例
以下是结构类型 Person
的示例
class Record(elems: (String, Any)*) extends Selectable:
private val fields = elems.toMap
def selectDynamic(name: String): Any = fields(name)
type Person = Record {
val name: String
val age: Int
}
Person
类型向其父类型 Record
添加了一个细化,该类型定义了 name
和 age
字段。我们说细化是结构性的,因为 name
和 age
未在父类型中定义。但它们仍然作为类 Person
的成员存在。例如,以下程序将打印 "Emma is 42 years old."
val person = Record(
"name" -> "Emma",
"age" -> 42
).asInstanceOf[Person]
println(s"${person.name} is ${person.age} years old.")
本示例中的父类型 Record
是一个泛型类,可以在其 elems
参数中表示任意记录。此参数是类型为 String
的标签对和类型为 Any
的值的序列。当您将 Person
创建为 Record
时,您必须通过类型转换断言记录定义了正确类型的正确字段。 Record
本身类型太弱,因此编译器在没有用户帮助的情况下无法知道这一点。在实践中,结构类型与其底层泛型表示之间的连接很可能由数据库层完成,因此不会成为最终用户的关注点。
Record
扩展标记特性 scala.Selectable
并定义一个方法 selectDynamic
,该方法将字段名映射到其值。通过调用此方法来选择结构类型成员。Scala 编译器将 person.name
和 person.age
选择翻译为
person.selectDynamic("name").asInstanceOf[String]
person.selectDynamic("age").asInstanceOf[Int]
第二个示例
为了加深你刚才看到的知识,这里有另一个名为 Book
的结构类型,它表示你可能从数据库中读取的一本书
type Book = Record {
val title: String
val author: String
val year: Int
val rating: Double
}
与 Person
一样,这是你创建 Book
实例的方式
val book = Record(
"title" -> "The Catcher in the Rye",
"author" -> "J. D. Salinger",
"year" -> 1951,
"rating" -> 4.5
).asInstanceOf[Book]
Selectable 类
除了 selectDynamic
,Selectable
类有时还会定义一个方法 applyDynamic
。然后可以使用此方法来翻译结构成员的函数调用。因此,如果 a
是 Selectable
的一个实例,则像 a.f(b, c)
这样的结构调用将转换为
a.applyDynamic("f")(b, c)
此页面的贡献者
内容
- 简介
- Scala 特性
- 为什么选择 Scala 3?
- Scala 尝鲜
- 你好,世界!
- REPL
- 变量和数据类型
- 控制结构
- 领域建模
- 方法
- 一等函数
- 单例对象
- 集合
- 上下文抽象
- 顶级定义
- 总结
- 类型初探
- 字符串插值
- 控制结构
- 领域建模
- 工具
- OOP 建模
- FP 建模
- 方法
- 方法特性
- Scala 3 中的主方法
- 总结
- 函数
- 匿名函数
- 函数变量
- Eta 展开
- 高阶函数
- 编写你自己的 map 方法
- 创建返回函数的方法
- 总结
- 打包和导入
- Scala 集合
- 集合类型
- 集合方法
- 总结
- 函数式编程
- 什么是函数式编程?
- 不可变值
- 纯函数
- 函数即值
- 函数式错误处理
- 总结
- 类型和类型系统
- 推断类型
- 泛型
- 交集类型
- 并集类型
- 代数数据类型
- 协变
- 不透明类型
- 结构类型
- 依赖函数类型
- 其他类型
- 上下文抽象
- 扩展方法
- 上下文参数
- 上下文边界
- 给定导入
- 类型类
- 多重相等
- 隐式转换
- 总结
- 并发
- Scala 工具
- 使用 sbt 构建和测试 Scala 项目
- 工作表
- 与 Java 交互
- 面向 Java 开发人员的 Scala
- 面向 JavaScript 开发人员的 Scala
- 面向 Python 开发人员的 Scala
- 下一步