關於Scala的路徑依賴類型(Path-dependent type)

         咋看這個術語,有點嚇倒。其實不是什麼新東西,也是講關於內部類和外部類的事,不過二者有點區別。在Scala中,內部類和外部類的一些行爲特性和Java差很少。 請看以下的Scala代碼:java

class Outer {
    private val x = 10
    class Inner {
        private val y = x + 10
    }
}

val outer = new Outer
val inner =  new outer.Inner
// inner變量能夠顯式聲明類型
val inner: outer.Inner = new outer.Inner

 內部類Inner能夠存取外部類成員(包括private成員),但外部類不能存取內部類的private成員. 內部類之因此能存取外部類的成員,是由於內部類隱含地持有外部類的實例。這點和Java是同樣的,上述的代碼能夠用Java語言來表達: ide

public class Op {

    public void test1(){
        Outer outer  = new Outer();
        Outer.Inner inner = outer.new Inner();
        System.out.println(inner);
    }

    public static void main(String[] args) {
        Op op = new Op();
        op.test1();
    }
}

class Outer {
    private int x = 10;
    class Inner {
        private int y = x + 10;
    }
}

注意Java和scala在建立成員內部類時的細微差異。

        Scala的內部類比Java的內部類具備更多特性,所以Scala引入了路徑依賴類型(Path-dependent type)的概念。像outer.Inner這樣的類型就稱爲路徑依賴類型。所謂路徑,指的就是參考外部類所創建的實例的名稱。 就outer.Inner這個類型來講,路徑爲outer。更重要的是,不一樣路徑表明不一樣的類型。好比:spa

val o1 = new Outer
val o2 = new Outer
val i1 =  new o1.Inner
val i2 =  new o2.Inner

上述的i1,i2分別引用了不一樣類型的實例,其中i1所引用的實體類型是o1.Inner, i2引用的實體類型是o2.Inner。假如你嘗試把o1.Inner的實例賦值給聲明爲o2.Inner類型的變量,編譯器會報錯:

val o1 = new Outer
val o2 = new Outer
val i: o2.Inner = new o1.Inner   // 編譯錯誤,類型不匹配

注意: 路徑依賴的是路徑的名稱,與路徑所引用的實例無關,請看例子: 

val o1 = new Outer
val o2 = o1
val i1: o1.Inner = new o1.Inner
val i2: o2.Inner = new o1.Inner   // 編譯錯誤。儘管o2和o1引用同一個實例,但o1.Inner和o2.Inner是不一樣的,Scala編譯器只根據路徑名進行區分

事實上, o1.Inner和o2.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

 路徑依賴類型能夠被繼承,請看例子scala

val o = new Outer
class Some extends o.Inner
val oi: Outer#Inner = new Some

 注意:Outer#Inner是不能被繼承的code

 此外,路徑依賴類型所在的路徑必須是不可變的值。例如繼承

var o1 = new Outer val i = new o1.Inner // 編譯錯誤。由於o1是var

這也很好理解,若是o1是var,那麼他的類型將是不肯定的,從而致使其路徑依賴類型也不肯定。

通常地,x0.x1.x2...xn.T是路徑一類類型,只要知足:編譯器

a) x0 是不可變值(即x0是val所聲明的變量)編譯

b) x1, x2, ..., xn 都是不可變的屬性class

c) T 是xn內部類 test

 

下面看一個負責的例子:

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)  // 吃魚

對於Cat類而言,F是其類型成員之一, 但這裏 cat1.F和cat2.F表明相同的類型,也就是說你能夠把一個cat2.F類型的實例傳遞給一個聲明爲cat1.F類型的變量. 實際上,上述代碼的最後四行等價於如下的代碼:

type F1 = Fish
type F2 = Fish
cat1.eat(new F1)   // 吃魚
cat1.eat(new F2)   // 吃魚

固然new Cat#F也是合法的

說了這麼多,大家以爲這種特性有什麼用途?若是你知道,請評論一下。

相關文章
相關標籤/搜索