2012-12-23 38 views
0

可能是斯卡拉大師的一個簡單的。我正在閱讀類型約束的約束,並想知道我是否錯過了一些東西,因爲我看到一個稍微意外的行爲。比方說,我們有三種類型的A,B,C在這樣的層次,爲此,我們正在與類型界限的實驗:斯卡拉類型邊界行爲(方法vs類)

class A { 
} 

class B extends A { 
} 

class C extends B { 
} 

class MyList1 { // T super B 
    def add[T >: B](a: T): Unit = {} 
} 

class MyList2 { // T extends B 
    def add[T <: B](a: T): Unit = {} 
} 

class MyList3[T >: B] { // T super B 
    def add(a: T): Unit = {} 
} 

class MyList4[T <: B] { // T extends B 
    def add(a: T): Unit = {} 
} 

object BoundsClass { 

    def main(args: Array[String]) { 
    val l1 = new MyList1 
    l1.add(new A) 
    l1.add(new B) 
    l1.add(new C) // why is this allowed?? 

    val l2 = new MyList2 
    // l2.add(new A) // not allowed (OK) 
    l2.add(new B) 
    l2.add(new C) 

    val l3a = new MyList3[A] 
    val l3b = new MyList3[B] 
    // val l3c = new MyList3[C] // not allowed (OK) 

    // val l4a = new MyList4[A] // not allowed (OK) 
    val l4b = new MyList4[B] 
    val l4c = new MyList4[C] 
    } 

} 

的集合表現爲預期所有除一個案例,看看l1.add (新C);不會導致編譯錯誤。爲什麼這是允許的?

謝謝!

回答

3

我不是自稱是斯卡拉大師,但我會刺穿這個。

您已經定義沿A >: B >: C線的類型層次,這樣你就可以做的東西一樣

val aa: A = new A 
val ab: A = new B 
val ac: A = new C 

val bb: B = new B 
val bc: B = new C 

val cc: C = new C 

就像你可以說val a: Any = new Whatever

所以,當你嘗試添加new Cl1,它就會像對待B實例,因爲這是自身符合該方法的類型界限最近的可用版本。

+0

如果試過這兩個:「val c1:A = new C」和「val c2:C = new C」。我認爲第二個不應該(因爲集合「l3c」)因爲約束是「T是B的超類型」,但是c2顯然是這裏的子類型嗎?但是再次沒有錯誤...... – Nikolaos

+0

'c2'是'C'的*實例*,由於繼承是如何工作的,它也是'B'(和'A')的一個實例,所以它不是錯誤。 – Dylan

0

以這種方式使用下邊界是沒有用的。你不能強制一個值參數有一個下限,因爲根據Liskov substitution principle,你必須能夠在需要類的實例的任何地方使用子類的實例。因此,您的代碼編譯,因爲new C可以被視爲B的實例。

由於這個原因,對方法的值參數設置下限通常是沒有用的。在你的第二種情況下,下界在你綁定類的類型參數的地方肯定更有用。所有類型參數都以編譯類型解析,因此,您可以期望編譯器在第二種情況下產生編譯器錯誤。你可以找到一個更具體的例子,下限使用here