1.6. 类型系统

Scala 拥有强大的静态类型系统,能够在不牺牲表达能力的前提下提供类型安全。

1.6.1. 泛型

泛型允许定义适用于多种类型的类、特质和方法:

// 泛型类
class Box[A](value: A):
  def get: A = value

val intBox  = Box(42)
val strBox  = Box("Hello")
// 泛型方法
def identity[A](x: A): A = x
identity(42)        // 推断为 Int
identity("Hello")   // 推断为 String

1.6.1.1. 泛型约束

上界(Upper Bound):限定类型必须是某个类型的子类型:

// A 必须是 Ordered[A] 的子类型
def max[A <: Ordered[A]](a: A, b: A): A = if a >= b then a else b

下界(Lower Bound):限定类型必须是某个类型的父类型:

// B 必须是 A 的父类型
def coerce[A, B >: A](a: A): B = a

上下界可以结合使用:

// [A >: Low <: High]

1.6.2. 协变、逆变与不变

修饰符

名称

含义

示例

+T

协变

Container[Parent] 可引用 Container[Child]

List[+T], Option[+T]

-T

逆变

Container[Child] 可引用 Container[Parent]

Function1[-T, +R] 中参数类型

T

不变

只能持有精确类型

Array[T], mutable.Set[T]

class Animal
class Dog extends Animal

// 协变
val dogs: List[Dog] = List(Dog())
val animals: List[Animal] = dogs      // OK

// 逆变:函数参数是逆变的
val f: Dog => Dog = identity
val g: Animal => Dog = f              // OK(Animal => Dog 可替代 Dog => Dog)

示例:自定义协变容器:

sealed trait MyList[+A]:
  def head: A
  def tail: MyList[A]
  def ::[B >: A](elem: B): MyList[B] = MyCons(elem, this)

case object MyNil extends MyList[Nothing]
case class MyCons[A](head: A, tail: MyList[A]) extends MyList[A]

1.6.3. 类型约束

1.6.3.1. 上下文界定(Context Bound)

// 要求存在 implicit/given Ordering[A]
def sort[A: Ordering](list: List[A]): List[A] =
  list.sorted

1.6.3.2. 视图界定(View Bound,Scala 2 风格,Scala 3 中移除)

// Scala 2 写法(已废弃)
// def max[A <% Ordered[A]](a: A, b: A): A = ...

// Scala 3 使用 given/using 替代
def max[A](a: A, b: A)(using ord: Ordering[A]): A =
  if ord.gteq(a, b) then a else b

1.6.4. 给定实例与 using 子句(Scala 3)

Scala 3 用 given / using 替代了 Scala 2 的 implicit:

// 定义给定实例
given intOrdering: Ordering[Int] with
  def compare(x: Int, y: Int): Int = x - y

// 使用 given 实例的 using 子句
def maxList[A](list: List[A])(using ord: Ordering[A]): A =
  list.max

// 调用时无需传入 using 参数(编译期自动解析)
maxList(List(3, 1, 4, 1, 5))    // 5

上下文参数简写:

def maxList[A: Ordering](list: List[A]): A =
  // summon[Ordering[A]] 获取 given 实例
  list.max

1.6.4.1. 类型类模式

类型类(Type Class)是 Scala 中的经典设计模式,结合了泛型和 given/using:

// 1. 定义类型类的 trait
trait Show[A]:
  def show(a: A): String

// 2. 提供给定实例
given Show[Int] with
  def show(a: Int): String = a.toString

given Show[String] with
  def show(a: String): String = s""""$a""""

// 3. 使用类型类
def printShow[A](a: A)(using s: Show[A]): Unit =
  println(s.show(a))

printShow(42)           // 42
printShow("Hello")      // "Hello"

1.6.5. 联合类型与交集类型(Scala 3)

1.6.5.1. 联合类型(Union Type)

表示值可以是多种类型之一:

type Result = Int | String

def handle(result: Result): String = result match
  case i: Int    => s"整数: $i"
  case s: String => s"字符串: $s"

1.6.5.2. 交集类型(Intersection Type)

表示值必须同时满足多种类型:

trait Readable:
  def read: String

trait Writable:
  def write(data: String): Unit

def process(rw: Readable & Writable): Unit =
  val data = rw.read
  rw.write(data)

1.6.6. 不透明类型(Opaque Type)

不透明类型为现有类型提供类型安全的包装,运行时无开销:

object Domain:
  opaque type UserId = Long
  object UserId:
    def apply(id: Long): UserId = id
    extension (id: UserId) def value: Long = id

import Domain.UserId
val uid: UserId = UserId(42)
// val bad: Long = uid   // 编译错误!不能在不透明类型定义域外解包