Scala在高阶类型的使用中,有三种变化,分别是顺变、协变及逆变,下面详细讲讲它们的含义与应用。
1. 高阶类型
高阶类型是接受其他参数用来构造新类型的,所以又称为“类型构造器”,用type关键字来定义:
// 定义高阶类型
type Foo[T] = Function0[T]
// 声明高阶类型变量
val foo: Foo[Int] = () => 1
// 输出1
println(foo())
高阶类型一个重要特点是,在参数化之前,它不是一个完整的类型,当然也就无法实例化了。
2. 不变
不变是指高阶类型的参数不能改变,所以,这当然是满足型变的,完全合法,如下:
val ln: List[String] = List("yiifaa")
# 参数一致,赋值显然是合法的
val l: List[String] = ln
3. 协变
协变是指把类型参数转换为参数的父类,创建方法是类型参数前加个“+”号,无需额外操作,如下:
case class Person[+T](username: T)
// 声明类型变量
val yiifaa: Person[String] = Person("yiifaa")
val yiifee: Person[Any] = yiifaa
如果不加“+”号会怎样?那就会产生类型不匹配错误,不能适应协变,只能“不变”了。
4. 逆变
逆变是相对于协变而言的,就是把类型参数转换为参数的子类,创建方法是在类型参数前加个“-”。将父类转换为子类是存在风险的,所以还需要添加转换函数,如下:
class Person[-T](username: T) {
// 逆变时参数要放在函数上
def apply(username: T): Person[String] = new Person(username)
}
val yiifee: Person[Any] = new Person("yiifaa")
// 逆变发生了
val yiifaa: Person[String] = yiifee
逆变主要应用于Function对象,其他地方较少。
总结
所有的类型编程都必须要在编译时确定是否合法,因为类型信息只有在编译时才是可知的(编译后会丢失),最后它们的关系图如下。