2015年3月28日 星期六

Scala knowledge - Nest class and # operator

不算前言
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時得多注意

沒有留言:

張貼留言