1. 简介

Scala 是一门静态类型语言,这意味着变量的类型在编译期就已经确定。

类型声明是 Scala 提供的一个特性,允许我们自定义类型

在这篇文章中,我们将学习如何使用 type 关键字进行类型声明。首先,我们会介绍如何使用它来创建类型别名;然后,我们会讲解抽象类型成员的声明与实现。

2. 类型别名(Type Alias)

类型别名主要用于简化复杂类型的书写,比如参数化类型(泛型)或函数类型

下面通过几个例子来说明其用法,并指出一些非法使用的场景。

2.1. 参数化类型的别名

我们可以通过类型别名为 List[Int] 创建一个简写:

object ListIntFunctions {
  type IntItems = List[Int]
}

这里,我们将 IntItems 定义为 List[Int] 的别名。这样做的好处是可以提升代码可读性,并集中管理类型定义。

在该对象的方法中使用这个别名:

def mean(items: IntItems): Double = {
  items.sum.toDouble / items.length
}

我们可以看到,items 可以调用 List 的方法,如 sumlength

如果要在 ListIntFunctions 外部调用 mean 方法,也可以直接使用 List[Int] 作为参数类型:

val intList = List(3, 6, 2, 2)
assert(ListIntFunctions.mean(intList) === 3.25)

当然,如果你自己也定义了 SomeInts = List[Int],那么也可以互换使用:

type SomeInts = List[Int]
val intList: SomeInts = List(3, 6, 2, 2)
assert(ListIntFunctions.mean(intList) === 3.25)

✅ 总结:List[Int] 和它的别名可以自由替换使用。

2.2. 函数类型的别名

另一个常见的用途是为函数类型创建别名。例如,我们可以为接受 Int 并返回 String 的函数定义一个别名:

type IntToString = Int => String

接着,在方法中使用这个别名:

def IntItemsToString(items: IntItems, intToString: IntToString): List[String] = {
  items.map(intToString)
}

为了演示,先定义一些数据:

val intList = (1 to 3).toList
def getChicken(item: Int): String = {
  s"$item chicken"
}

然后调用方法:

val stringList = ListIntFunctions.IntItemsToString(intList, getChicken)
assert(stringList === List("1 chicken", "2 chicken", "3 chicken"))

✅ 每个数字都被转换成了 “x chicken”。

2.3. 非法的类型别名

⚠️ 在使用类型别名时需要注意以下几点:

不能创建自引用的类型别名

scala> type A = List[A]
<console>:11: error: illegal cyclic reference involving type A

不能省略参数化类型的类型参数

scala> type T = List
<console>:11: error: type List takes type parameters

不能从复合类型中选取部分字段(比如 Tuple):

scala> type Y = Tuple2[Int, String]
defined type alias Y

scala> type Z = List[Y.key]
<console>:11: error: not found: value Y

3. 抽象类型成员(Type Member)

抽象类型成员可以在对象、类或特质中声明,并在其作用域内使用。最常见的用法是在特质中声明抽象类型,然后在实现类中指定具体类型。

3.1. 声明抽象类型成员

假设我们要创建一些执行重复操作的对象,这些对象都遵循同一个契约。我们可以为此定义一个特质:

trait Repeat {
  type RepeatType
  def apply(item: RepeatType, reps: Int): RepeatType
}

在这里,RepeatType 是一个抽象类型,需要在实现类中具体化。同时,我们还定义了一个 apply 方法,用于根据类型实现不同的行为。

3.2. 实现抽象类型成员

接下来我们看看如何在类中实现这个抽象类型。

Int 类型为例:

object IntegerRepeat extends Repeat {
  type RepeatType = Int
  def apply(item: RepeatType, reps: Int): RepeatType = {
    (item.toString * reps).toInt
  }
}

测试一下效果:

assert(IntegerRepeat(3, 5) == 33333)
assert(IntegerRepeat(10, 3) == 101010)

✅ 这个对象会把整数重复拼接成新的整数。

再来看一个处理 List 的实现:

object ListRepeat extends Repeat {
  type RepeatType = List[Any]
  def apply(item: RepeatType, reps: Int): RepeatType = {
    (1 to reps).map(_ => item).reduce(_ ::: _)
  }
}

测试一下:

assert(ListRepeat(List("a", "b"), 2) == Seq("a", "b", "a", "b"))
assert(ListRepeat(List(1), 3) == Seq(1, 1, 1))

✅ 虽然两个对象都实现了 Repeat 特质,但行为完全不同。

4. 结论

本文我们深入了解了 Scala 中的 type 关键字。

我们学会了如何使用它来创建类型别名,从而简化复杂类型和函数类型的书写;还掌握了如何在特质中声明抽象类型成员,并在实现类中具体化它们。

一如既往,文中所有示例代码都可以在 GitHub 上找到。


原始标题:Type Declaration in Scala