路径依赖类型(Path-dependent types) 不知道AbsCell绑定的类型情况下,也可以对其进行访问。下面这段代码将一个cell的值恢复成为其初始值(init),而无需关心cell值的类型是什么。
def reset(c: AbsCell): unit = c.set(c.init)
为什么可以这样做呢?因为c.init的类型是c.T,而c.set是c.T=>unit类型的函数,因此形参与实参类型一致,方法调用是类型正确的。
c.T是一个路径依赖类型的例子,通常来讲,这种类型的形式是:x1.x2.….xn.T(n>0),x1,…,xn是不可变的值,而T是xn的类型成员。路径依赖类型是Scala的一个新颖的特性,其理论基础是vObj calculus[36]。
路径依赖类型要依靠其前缀路径的不可变性,下面给出一个违反了不可变性的例子:
var flip = false
def f(): AbsCell = {
flip = !flip
if (flip) new AbsCell { type T = int; val init = 1 }
else new AbsCell { type T = String; val init = "" }
}
f().set(f().get) // illegal!
在上例中,每一次调用f()分别返回int和String类型的值,因此最后一句是错误的,因为它要将String类型的值赋给一个int值的cell。Scala类型系统禁止这种调用,因为f().get的类型是f().T,而这不是一个有效类型,因为f()不是一个有效路径。
类型选择与单例类型(Type selection and singleton types)在Java中,类型定义可以嵌套,嵌套类型用其外部类型做前缀的形态表示。在Scala中,则通过“外部类型#内部类型”(Outer#Inner)的方式来表示,“#”就称作类型选择(Type Selection)。从概念上说,这与路径依赖类型(例如:p.Inner)不同,因为p是一个值,不是一个类型。进一步而言,Outer#t也是一个无效表达式,如果t是一个定义在Outer中的抽象类型的话。
实际上,路径依赖类型可以被扩展成为类型选择,p.t可以看做是p.type#t,这里p.type就称作单例类型,仅代表p所指向对象的类型。单例类型本身对于支持方法调用串接很有作用,考虑如下代码:C有一个incr方法,对其值+1,其子类D由一个decr方法,对其值-1。
class C {
protected var x = 0
def incr: this.type = { x = x + 1; this }
}
class D extends C {
def decr: this.type = { x = x - 1; this }
}
从而我们可以将相关调用串接起来:
val d = new D; d.incr.decr
如果没有this.type这个单例类型,上述调用是非法的,因为d.incr的类型应该是C,但C并没有decr方法。从这个意义上说,this.type类似于Kim Bruce的mytype[29]的一个协变的使用方式。