章节摘要:整条路线最高、最陡的一段。从泛型与型变(协变、逆变)开始,经抽象类型与结构类型,再到 Scala 3 用 given/using 重写的隐式机制,最终登顶类型类。读完这章,你看 Kotlin、Rust、TypeScript 的类型系统都会觉得眼熟——因为它们的许多设计正源于这里。
+A 与逆变 -A 标注金句:继承是"是什么"的纵向关系,类型类是"能做什么"的横向关系;型变回答的是"容器之间的关系该跟着元素关系走多远"。
四节是连续的岩缝组:泛型是基本握法(5.1),抽象类型换一个声明角度(5.2),given/using 让编译器替你传"证据"(5.3),类型类把前三者组装成一个可扩展架构(5.4)。任何一节松动都别急着上,主峰段每一步都踩在下一步上。
[泛型+型变] -> [抽象/结构类型] -> [given/using] -> [类型类] \________________________________________/ Spark 的 Encoder、Cats 的类型类、Tapir 的派生 —— 全是这条链的成品
前置:第四章集合(型变的例子全部来自集合与函数)。延伸:6.1 Future 的回调需要 ExecutionContext,正是 5.3 的 given 在并发里的日常亮相。
主峰的收获要落回工具。给 Reading 实现类型类化的比较与展示,是对 5.1 到 5.4 的总演习:
given Ordering[Reading] = Ordering.by(_.asCelsius) given Show[Reading] with extension (r: Reading) def show = f"${r.asCelsius}%.1f°C at ${r.scale.name}" val sorted = readings.sorted.take(1) // 最凉一条读数 readings.map(summon[Show[Reading]].show) // 全部可打印
两个 given 各自独立:换一种排序口径不影响展示,换一种展示不影响排序——能力正交,这正是类型类架构的日常体感。若用继承实现同样需求,Reading 会被迫实现 Comparable 与某个 Loggable 接口,两种能力搅在一条继承链里,扩展第三种能力就要再改一次类。