Nest class 在JAVA中是很常見的語法, 比如要嵌入實作某些Listener interface, 在JVM上的Scala 也是有Inner class應用方式, 只是當你在外部Call inner class的時候, 會很不一樣...
在Scala 中如果定義 Nested class從外部的存取狀況會很特殊
比如
class A {
class B
def f(b: B) = println("Got my B!")
}
然後去嘗試從外部存取
scala> val a1 = new A
a1: A = A@2fa8ecf4
scala> val a2 = new A
a2: A = A@4bed4c8
scala> a2.f(new a1.B)
<console>:11: error: type mismatch;
found : a1.B
required: a2.B
a2.f(new a1.B)
想說透過 a1 的Instance 來創造B, 因此 new al.B
然後把 new al.B 給 a2的 f(b: B) function
但a2的 f(b: B) function竟然不吃 a1.B
這是因為 a1.B 這個class 跟 a2裡面的 B是不一樣的!!
你可以說兩者code一樣, 為什麼會這樣
這是Scala另一個特性: path dependent types.
a1 跟 a2 是兩個不一樣的Instance
導致其內部的class B 實際上是不同的 B
以至於外部存取時 a1.B 跟 a2.B 都是不一樣的class
就當作是 class a1.B 與 class a2.B 兩個不同class
要更加了解得深入去研究 path dependent type
之後會有文章介紹
有一個做法傳 a2.B 給 a2.f
或者透過 # 符號
在Scala中 # 是讓你提取 nested class出來讓外部使用
簡單來說在JAVA中要抓到B class
透過逗號, 比如 a1.B , a2.B
從外部抓到B class, 且兩者一樣
但在Scala中外部code透過逗號提取nested class
會被 path dependent type處理成不同class B
這時候你要做跟JAVA一樣事情時且避免 path dependent type
就是透過 #
比如
class A {
class B
def f(b: B) = println("Got my B!")
def g(b: A#B) = println("Got a B.")
}
定義Function g(b: A#B), g那邊指名說我要的class B是透過 A#B
就是從任何一個Instance A 提取出來的class B 我都接受
所以可以看到
scala> val a1 = new A
a1: A = A@1497b7b1
scala> val a2 = new A
a2: A = A@2607c28c
scala> a2.f(new a1.B)
<console>:11: error: type mismatch;
found : a1.B
required: a2.B
a2.f(new a1.B)
^
scala> a2.g(new a1.B)
Got a B.
這時候 a2.g 吃的 a1.B 就可以了
我是解釋為
既然提取B得從一個Instance A提取
但是不同Instance A提出來的都是, 一個獨立新的B
每個Instance A.B 都是不一樣的
然後 b: A#B算是一個進階的sugar syntax
不管任何Instance A 他們的B我都視唯同一個B
(本來就是, 只是被path dependent type改掉而已)
以上
用nested class時得多注意
沒有留言:
張貼留言