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
的方法,如 sum
和 length
。
如果要在 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 上找到。