路徑相依型態(Path-dependent type)


類別中可以有類別,基本上稱之為內部類別(Inner class),例如:
class Outer {
private val x = 10
class Inner {
private val y = x + 10
}
}

內部類別可以存取外部類別的成員(包括private成員),而外部類別無法存取內部類別的private成員,如果你想要外部類別可以存取內部類別的private成員,可以參考 存取修飾 的介紹。

內部類別之所以能存取外部類別的成員,是因為內部類別會隱含地參考至外部類別所建構的實例,也因此如果你要使用內部類別來建構實例時,必須先建立外部類別物件(如此才有外部類別實例可以參考)。一個例子如下所示:
val outer = new Outer
val inner = new outer.Inner

那麼內部類別的型態是什麼?又或者問inner參考名稱的型態是什麼?答案是outer.Inner!你確實是可以使用outer.Inner來宣告型態的:
val outer = new Outer
val inner: outer.Inner = new outer.Inner

outer.Inner這樣的型態,稱之為路徑相依型態(Path-dependent type),所謂路徑(Path),指的是參考住外部類別所建立實例的名稱,就outer.Inner這個型態來說,其路徑為outer。之所以稱之為路徑相依型態,是因為不同的路徑,就代表著不同的型態。例如:
val o1 = new Outer
val o2 = new Outer
val i1 = new o1.Inner
val i2 = new o2.Inner

在上例中,o1.Inner與o2.Inner是不同的型態,i1的型態是o1.Inner,i2的型態是o2.Inner。所以下例中,不能通過編譯:
val o1 = new Outer
val o2 = new Outer
val i: o2.Inner = new o1.Inner // 編譯錯誤,type mismatch

注意!相依的是路徑的名稱,不是路徑所參考的物件。例如:
val o1 = new Outer
val o2 = o1
val i1: o1.Inner = new o1.Inner
val i2: o2.Inner = new o1.Inner // 編譯錯誤,type mismatch

在上例中,o2與o2參考的雖然是同一物件,然而o1.Inner與o2.Inner依然是不同的型態。事實上,o1.Inner、o2.Inner等型態,都是一種Outer#Inner,也就是Outer#Inner的子類型:
val o1 = new Outer
val o2 = new Outer
val i1: Outer#Inner = new o1.Inner
val i2: Outer#Inner = new o2.Inner

你要拿路徑相依型態來繼承也是可行的:
val o = new Outer
class Some extends o.Inner
val oi: Outer#Inner = new Some

再來看看 型態(type)成員 中的一個例子:
class Food

class Fish extends Food {
override def toString = "魚"
}

abstract class Animal {
type F <: Food
def eat(f: F)
}

class Cat extends Animal {
type F = Fish
def eat(fish: Fish) {
println("吃" + fish)
}
}

val cat1 = new Cat
val cat2 = new Cat
cat1.eat(new cat1.F) // 吃魚
cat2.eat(new cat2.F) // 吃魚
cat1.eat(new cat2.F) // 吃魚
cat2.eat(new cat1.F) // 吃魚

對於Cat而言,F是其類別成員之一,實際上cat1.F與cat2.F代表著兩個不同的別名,這也是路徑相依的一個例子,只不過在上例中,cat1.F與cat2.F這兩個名稱,剛好都是被指定為Fish的別名,所以上例中最後兩行執行是沒有問題的,就像是以下的例子:
type F1 = Fish
type F2 = Fish
cat1.eat(new F1) // 吃魚
cat1.eat(new F2) // 吃魚

實際上,無論是別名cat1.F或是別名cat2.F,都是Cat#F的特例:
val cat = new Cat
cat.eat(new Cat#F) // 吃魚

路徑相依型態的應用,可以繼承參考 列舉(Enumeration) 的說明。