//reflect.java  --- @CopyLeft by tsaiwn@csie.nctu.edu.tw
//Demo Java Reflection --- Dynamic type checking  (java.lang.reflect.*)
import java.util.*;    // import java.lang.reflect.*
class reflect{
   static java.io.PrintStream cout = System.out;
   public static void main(String xxx[ ]) {
       int a = 38;  int x[ ] = { 33, 88, 58};
       Integer y[ ] = { 33, 88, 58, 888, 567, 666, 555, 258};
       String s = "abced";   // String (字串) 是 Object
       String sa[ ] = { "abc", "33", "888", "ggg", "58"}; // array
       Class ic = int.class;  print("ic: "); test(ic);   //long.class, ...
       print("x: "); test(x);     // x[ ] 是 Primitive 的 Array
       print("y: "); test(y);     // y[ ] 是 Integer Object 的 Array
       print("s: "); test(s);    // is a String Object
       print("sa: "); test(sa);   // is an array of String
       Student stu = new Student( );   // a Student
       print("stu: "); test(stu);
       Student[ ]stuary = new Student[6];   //注意 六個元素都是空的 (null)
       // stuary[0] = new Student( );     // enable 這句再測試看看
       print("stuary: "); test(stuary);    // 把上列的 new Enable 再試試看
       try{  
          Class tt = Class.forName("Student"); // 給我字串我也可生出該 class
          Object gg= tt.newInstance( );       // 生出一個 Student
          print("gg: "); test(gg);         // gg is a Student now
          Class ggType = gg.getClass( );   // any Object can use getClass( )
          print(" is gg an instance of Student? ");
          println( "" + ggType.isInstance(new Student( ) ) );
          cout.print(" Fields name of gg: "); pfd(gg);
       }catch(Exception e){;}    // 學警察吃案 :-)
   } //main(
   static void print(String s) { cout.print(s); }  // 方便程式中使用 print( )
   static void println(String s) { cout.println(s); }
   static void printf(String s, Object... y) { cout.printf(s, y); }
   static void test(Object x) {
      Class whatWG = x.getClass( );   // x 是 蝦密碗糕  
      print( "type name=" +whatWG.getName( ) + " ; ");
      if(whatWG.isArray( ) ) {
         Class elementType = whatWG.getComponentType();
         print("\n    is Array of " + elementType);
         print(" ; Array size: " + java.lang.reflect.Array.getLength(x));
         if(elementType.isPrimitive( ) ) println(", element is Primitive");
         else {  // is Object
            print(", element NOT Primitive\n  --> its element: ");
            try{
               Object y = java.lang.reflect.Array.get(x, 0); //x[0]
               test(y);  // recursice call my self
            }catch(Exception e){ println(" x[0] NOT exist!  null ?");}
         }//if..else
      } else println(" NOT an Array");   // if .. isArray(
   }//test(
   static void pfd(Object x){        // 可取得 class 內各 field name
      Class whatWG = x.getClass( );   // x 是 蝦密碗糕  
      java.lang.reflect.Field fd[ ]= whatWG.getDeclaredFields( );
      for(int i=0; i< fd.length; ++i) cout.print(" "+fd[i].getName( ));
      cout.println( );      // 也可取得 class 內各 函數名稱
      java.lang.reflect.Method m[ ] = whatWG.getDeclaredMethods( );
      cout.print(" methods: ");
      for (int i = 0; i < m.length; i++)cout.println(m[i].toString());
      print("  --- the Class name is "+ whatWG.getName( ) );
   }//pfd(           // also try  getDeclaredMethods( ),  getMethods( )
}//class 
class Student implements Comparable<Student>{
    String id, name;
    int height=0; float wet=0;
    public int compareTo(Student b){return height - b.height;}
    protected Integer haha( ) { return (Integer)38; }
}; 

/**********
D:\COURSE\OOP\ppnt>javac reflect.java
D:\COURSE\OOP\ppnt>java reflect
ic: type name=java.lang.Class ;  NOT an Array
x: type name=[I ;
    is Array of int ; Array size: 3, element is Primitive
y: type name=[Ljava.lang.Integer; ;
    is Array of class java.lang.Integer ; Array size: 8, element NOT Primitive
  --> its element: type name=java.lang.Integer ;  NOT an Array
s: type name=java.lang.String ;  NOT an Array
sa: type name=[Ljava.lang.String; ;
    is Array of class java.lang.String ; Array size: 5, element NOT Primitive
  --> its element: type name=java.lang.String ;  NOT an Array
stu: type name=Student ;  NOT an Array
stuary: type name=[LStudent; ;
    is Array of class Student ; Array size: 6, element NOT Primitive
  --> its element:  x[0] NOT exist!  null ?
gg: type name=Student ;  NOT an Array
 is gg an instance of Student? true
 Fields name of gg:  id name height wet
 methods: protected java.lang.Integer Student.haha()
public int Student.compareTo(Student)
public int Student.compareTo(java.lang.Object)
  --- the Class name is Student                   ***************/
/****** 關於 Java Reflection 反射 ---  java.lang.reflection.*;
    顧名思義, Java reflection 就是可以自己知道自己是啥, 有如照鏡子!
這是指 Java 程式在執行時期(Run time) ..
可以動態檢查 (Dynamic checking)各變數與各類別的一些屬性!
可以先用 getClass( ) 再用Class內的getName( )取得物件所屬 class 的名稱;
透過該 Class 物件, 我們可以查知該類別隸屬於那個Package、
類別本身究竟是Public還是Private、繼承自那一類別、實作了那些介面等。
甚至我們可以查知它究竟有哪些成員變數(欄位)以及哪些成員函數（包括建構子）。
    我們可以查知物件是不是 array? 然後搭配 java.lang.reflect.Array 的函數!
若是 array 則可用 getComponentType() 取得其元素的 type (也是 Class 物件); 
接著可用java.lang.Class 內的isPrimitive( )看看其元素是否為原始型別, 
若不是原始型別(Primitive data type; 有八種)當然就是某物件,
這時可再看看該物件是否為 Array ? (可 recursively 檢查)
    對於任何類別, 可以取得其宣告哪些來欄位(變數), 以及哪些函數;
更可取得各變數欄位(Field), 也可取得它的各函數(Method)來使用!
另外, 只要用字串指定 class 名稱, 就可用Class.forName(名稱)生出該類物件!
這樣可以讓使用者輸入他想要生出的物件之名稱!
當然要用 try{ } catch( ) block 避免用到不存在的 class name!

重點: (江湖一點訣:-)
   * 記住 Java 寫的任何 class 都是直接或間接 extends java.lang.Object
   * 所有的物件(Object) 都可以叫用 getClass( ) 取得其類別
     自己試試把 primitive 傳給這寫的 pfd( ) 看看; 例如 試試 pfd(123);
     (別忘了 Java JDK1.5 之後有 Auto box/unbox 能力)
   * 此時取得的物件是一個 java.lang.Class 物件
      java.lang.Class 是一個很好用的 class, 裡面有六十多個函數;
      (在C#中有一個相似的類別，則叫做Type 不叫 Class)
   * 這叫 Class 的類別裡面有很多函數可以用, 例如 isArray( )可看是否為陣列
   * 若是陣列可用 java.lang.reflect.Array 內許多好用的 static 函數,
     例如 Array.getLength(array Object),  get( )可取得array某元素
   * java.lang.Class 內的數十個 getXXX( ) 可用來抓 Field, Method, ...
     所以 java.lang.reflect.* 還有 Field, Method 等對應的類別,
     透過 Field內函數可以取得或更改物件變數各欄位的值,
     甚至可以取得物件變數各函數名稱或是要求執行某函數!
   * 可用 Class.forName("Stack") 生出 Stack 的 Class 物件,
     再用該 Class 物件.newInstance( ); 就可生出 Stack 物件;
     此處的 "Stack" 可換成任何 class name
     就是說程式可以依照使用者輸入或是從檔案讀入資料來動態地決定究竟要
     產生那個類別的物件。 (參看我給的Java範例目錄中的 33_forname/)
   * java.lang.reflect.Constructor 可用來取得 Constructor 以便執行,
   * 有了 reflection, 程式碼在撰寫時就不必將行為寫死，包括要處理的類別、
     要存取的成員變數、要呼叫的成員函數等，都可動態決定。
****************************************************/
