2011年5月7日 星期六

5-6. Inner class

Inner class 在 Java 中時常被使用,在 Scala 中也一樣很常被使用。

Java 宣告 inner class 的方式,是在 class 中 宣告另一個 class。
Inner class 被編譯後,會產生 outer_class$inner_class 的 classfile。
甚至在 method 中也可以宣告 inner class。

範例:宣告 inner class
class J1 {
  class J11 { //inner class
    void print() {
      System.out.println("J11");
    }
  }
}
上例會產生兩個 class file
1. J1.class
2. J1$J11.class:代表 J11 的 inner class。

範例:在 method 中宣告 inner class,此時只有該 method 才知道這個 inner class。
class J1 {
  void p1() {
    class J11 { //method 中 的 inner class
      void print() {
        System.out.println("p1");
      }
    }
  }
  void m1() {
    J11 j11 = new J11();//這裡是錯誤的,因為 m1() 看不到 J11
  }  
}
上例會產生兩個 class file
1. J1.class
2. J1$J11.class:代表 J11 的 inner class。
但由於 J11 是宣告在 J1.p1() method 裡面,所以在 m1() 中將看不到 J11 這個 class。

Inner class 的 instance 隱含有一個 outer class instance 的 reference。也就是,若要 create 一個 inner class instance,需要有一個 outer instance 才能成功 create。若在其他 class 要 create inner class 的 instance,需要使用特殊的語法。
範例:inner class instance 的 creation。
class J1 {
  class J11 { //inner class
    void print() {
      System.out.println("J11");
    }
  }
  void m1() {
    J11 j11 = new J11();//這裡 create 一個 J11,好像沒有 outer class instance
//但其實在 instance method 中呼叫,已經隱含一個 outer class instance。
  }
  static public void main(String[] args) {
    J11 j11 = new J11();//這裡會出錯,因為 J11 的 instance 需要 outer class instance
    J1 j1 = new J1();
    J11 j11 = j1.new J11();//這裡使用 j1 來 create J11。
//請注意 j1.new J11() 這個特殊語法
  }
}

Scala inner class 用法,與 Java 的類似。我們看例子。
範例:宣告 inner class
class S1 {
  class S11 {
    def p1 = println("p1")
  }
}
上例會產生兩個 class file
1. S1.class
2. S1$S11.class:代表 S11 的 inner class。

請注意,Java 的 inner class 的實際 type,為:outer_class.inner_class,使用「.」dot。
但 Scala 的「.」dot,只能使用在 instance 上。compiler 一遇到 dot 就會試圖解釋為 instance 的 method 或data field。
所以,在 Scala,inner class 的實際 type,為 outer_class#inner_class。Scala 與 Java 不太相同,請注意。
class S1 {
  class S11 {
    def p1 = println("p1")
  }
}
object Main {
  def m1 {
    val s11: S1.S11 = null // 使用 S1.S11 當 type,但這是錯誤的。遇到「S1.xxx」時,compiler 會去找 S1 這個 object
    val s12: S1#S11 = null // S1#S11 才是正確的 type。
  }
}
範例:在 method 中宣告 inner class,Scala 的用法與 Java 相同。
class S1 {
  def p1 = {
    class S11 { // 在 method 中宣告 inner class
    }
  }
}
與 Java 相同,Scala inner class 的 instance,也是隱含一個 outer class 的 reference。所以要 create 一個 inner class 的 instance,需有一個 outer instance。
使用 outer instance 來 create inner instance 的語法,Scala 與 Java 不太相同。
class S1 {
  class S11 // 這個 class 沒有 body,所以可以省略大括號
}
class Main {
  val s1 = new S
  val s11 = new s1.S11 // 這裡的語法與 Java 不同,new 為 keyword,放在最前面
}
在 Scala 中,不同的 outer instance 所產生出來的 inner instance,type 是不相同的。
讓我們看下一例子。
class S {
  class Inner;
}
class Main {
  val s1 = new S
  val s1_inner = new s1.Inner  //s1_inner 的 type 為 s1.Inner
  val s2 = new S1
  val s2_inner = new S2.inner //s2_inner 的 type 為 s2.inner
}
上例的 s1_inner 與 s2_inner 都是 S#Inner 的 type。
但實質上,二者的 type 並不相同,一個是 s1.Inner type,另一個是 s2.inner type。這種與 outer instance 相關的 type,稱為 path-dependent type。

沒有留言:

張貼留言