一些用例,例如建模数据库访问,在静态类型语言中比在动态类型语言中更尴尬。
使用动态类型语言,很自然地将行建模为记录或对象,并使用简单的点表示法选择条目,例如 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
扩展了标记 trait 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]
可选类
除了 selectDynamic
之外,Selectable
类有时还会定义 applyDynamic
方法。
然后可以使用它来翻译是函数调用的结构成员。
因此,如果 a
是 Selectable
的一个实例,则像 a.f(b, c)
这样的结构调用将转换为:
a.applyDynamic("f")(b, c)
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
- 下一步去哪