Reflections on Java Reflection

By on Sep 14, 2008 in translated | 7 comments

Share On GoogleShare On FacebookShare On Twitter

Translated from : Reflections on Java Reflection
Author : Russ Olsen
Thanks to Russ who allow me to translate his blog here

Russ is also the author of Design Patterns in Ruby

ในชีวิตปกติ รีเฟลคชัน (reflection) คือภาพสะท้อนในกระจก แต่ในโลกของการเขียนโปรแกรมแล้ว รีเฟลคชันคือการที่โปรแกรม สามารถรู้ถึงโครงสร้างของตัวเอง และยังสามารถปรับเปลี่ยนโครงสร้างนั้นได้อีกด้วย Java reflection API มอบหน้าต่างให้คุณมองเข้าไปสู่ ค่าพื้นฐานต่างๆของภาษา นั่นคือ คลาส, ฟิลด์และเมท็อด ผ่านทาง Java API ธรรมดาง่ายๆ
การเข้าใจ รีเฟลคชัน จะช่วยให้คุณ เข้าใจเครื่องมือต่างๆ ที่คุณใช้อยู่ประจำวันเช่น eclipseทำ auto-completion ของชื่อเมท็อดได้อย่างไร หรือ Tomcat สามารถสร้าง servlet ขึ้นมาใช้งานได้ จากเพียงแค่ชื่อของคลาสใน web.xml ได้อย่างไร และ Spring ควบคุมการทำ dependency injection ราวกับมายากลนั้นได้อย่างไร ด้วยรีเฟลคชันคุณสามารถเขียนโค้ดที่มีความยืนหยุ่น ได้มากกว่าระดับไดนามิค นั่นคือโปรแกรมของคุณ สามารถจัดการกับคลาส ที่มันไม่เคยเจอมาก่อนเลยได้
การเรียก Class มาใช้งาน
ดังที่ผมได้พูดไปแล้วว่า แนวคิดพื้นฐานของรีแฟลคชัน คือการใช้ Java API ธรรมดาๆเข้าไปดูการทำงานข้างในของตัวโปรแกรม เนื่องจากสิ่งที่เป็นพื้นฐานที่สุดของจาวาคือคลาส (ลองเขียนจาวาโปรแกรมสักอันโดยไม่ต้องใช้คลาสดูสิ) ดังนั้นจุดเริ่มต้นที่ดีที่สุดก็คือการเรียนรู้คลาสที่ชื่อว่า “Class” ทุกคลาสในจาวาจะมีอ็อบเจกต์ตัวหนึ่งซึ่งเก็บข้อมูลทุกอย่างเกี่ยวกับคลาสนั้นไว้ อ็อบเจกต์นั้นเป็นอ็อบเจกต์ของคลาส Class คุณสามารถดึงเอาข้อมูลทุกอย่างของคลาสนั้นจากมันได้ (ต่อไปนี้จะขอเรียกอ็อบเจกต์ตัวนี้ว่า คลาสอ็อบเจกต์)เช่น ชื่อของคลาส หรือข้อมูลว่าคลาสนั้นเป็น public หรือ abstract หรือ final หรือไม่ หรือแม้กระทั่งข้อมูลของคลาสแม่ของมัน เรามาลองใช้รีเฟลคชันส่องดูคลาส Employee ง่ายๆอันนี้กัน

package com.russolsen.reflect;

public class Employee
{
   public String _firstName;
   public String _lastName;
   private int _salary;

      public Employee(){
        this( "John", "Smith", 50000);
      }
 
   public Employee(String fn, String ln, int salary){
      _firstName = fn;
      _lastName = ln;
      _salary = salary;
   }
   
   public int getSalary(){
      return _salary;
   }
   
   public void setSalary(int salary){
      _salary = salary;
   }
   
   public String toString(){
      return "Employee: " + _firstName +  " "
             + _lastName + " " + _salary;
   }

}

วิธีที่ง่ายที่สุดในการนำเอาคลาสอ็อบเจกต์มาใช้ ก็คือเพียงแค่ใช้เมท็อด getClass เรียกเอาคลาสอ็อบเจกต์ของอ็อบเจกต์ตัวนั้นออกมา โค้ดข้างล่างนี้สร้างอ็อบเจคต์ของ Employee ดึงเอาคลาสอ็อบเจกต์ของมันออกมา และทำการแสดงค่าข้อมูลบางอย่างของคลาสนั้น

package com.russolsen.reflect;
import java.lang.reflect.Modifier;

public class GetClassExample{

   public static void main(String[] args){
 
      Employee employee = new Employee();
      
      Class klass = employee.getClass();
      
      System.out.println( "Class name: " + klass.getName());
      System.out.println( "Class super class: " + klass.getSuperclass());
      
      int mods = klass.getModifiers();
      System.out.println( "Class is public: " + Modifier.isPublic(mods));
      System.out.println( "Class is final: " +  Modifier.isFinal(mods));
      System.out.println( "Class is abstract: " + Modifier.isAbstract(mods)); 
   }
}

ทำการสั่งประมวลผลโค้ดข้างบนนี้แล้วคุณจะเห็น:
Class name: com.russolsen.reflect.Employee
Class super class: class java.lang.Object
Class is public: true
Class is final: false
Class is abstract: false

ดังที่เห็นในตัวอย่าง การดึงข้อมูลชื่อของคลาสและคลาสแม่นั้น ง่ายพอๆกับการเรียกเมท็อดสำหรับดึงข้อมูลธรรมดาๆ ถ้าคุณอยากจะรู้ว่าคลาสนั้นเป็น public หรือ abstract หรือ final กระบวนการจะยากขึ้นเล็กน้อย ข้อมูลเหล่านั้นจะถูกแสดงอยู่ในรูปของ int ตัวหนึ่งซึ่งได้มาจากการเรียกเมท็อด getModifiers และคุณสามารถใช้สแตติคเมท็อดทั้งหลายในคลาส Modifier เพื่อช่วยตีความค่าตัวเลขนั้น

การเรียกเมท็อด getClass นั้น ไม่ได้เป็นทางเดียวในการเรียกใช้งานคลาสอ็อบเจกต์ คุณยังสามารถเรียกเอาคลาสอ็อบเจกต์ออกมาโดยตรงจากชื่อคลาสนั้นก็ได้

Class klass = Employee.class;

วิธีที่สามในการดึงคลาสออกมาใช้งานนั้น น่าสนใจทีเดียว คุณสามารถสร้างอ็อบเจกต์ของ Class จากสตริงได้ ซึ่งสตริงนั้นก็ต้องมีค่าเป็นชื่อของคลาสด้วย วิธีการก็คือเรียกเมท็อด forName ของคลาส Class

Class klass = Class.forName("com.russolsen.reflect.Employee");
      
System.out.println( "Class name: " + klass.getName());
System.out.println( "Class super class: " + klass.getSuperclass());
      
// Print out the rest...

สิ่งหนึ่งที่ต้องจดจำไว้เสมอก็คือ คุณจะต้องใช้ชื่อเต็มของคลาส ซึ่งเป็นชื่อที่รวมชื่อของ package ของคลาสนั้นด้วย คุณไม่สามารถใช้แค่ชื่อ “Employee” คุณจะต้องใช้ “com.russolsen.reflect.Employee” จากการใช้งานเมท็อด forName นั้นเราก็เริ่มจะเห็นพลังความสามารถพื้นฐานของรีเฟลคชัน นั่นคือคุณสามารถใช้ชื่อของคลาสในสตริง เพื่อสร้างคลาสอ็อบเจกต์เอามาใช้งานได้

สร้างอินสแตนได้ในทันที
การดึงเอาคลาสอ็อบเจกต์ออกมาเพื่อเรียกดูค่าต่างๆของมัน ก็มีความน่าสนใจ และมีประโยชน์ในตัวของมันเองอยู่แล้ว แต่การได้ ”ทำอะไรบางอย่าง” จริงๆกับคลาสนั้นเป็นสิ่งที่ทำให้รีเฟลคชัน เริ่มที่จะดูน่าตื่นเต้นขึ้น และสิ่งที่ชัดเจนที่สุดในการใช้งาน Class ก็คือการสร้างอินสแตนใหม่ของคลาสนั้น
วิธีที่ง่ายที่สุดก็คือการใช้เมท็อด newInstance โปรแกรมข้างล่าง ได้แสดงตัวอย่าง การรับตัวแปรจากภายนอกโปรแกรม (ซึ่งควรจะมีค่าเป็นชื่อของคลาสๆหนึ่ง) สร้างคลาสจากค่านั้นแล้วสร้างอินสแตนใหม่จากคลาสนั้น

package com.russolsen.reflect;

public class NewInstanceExample{
   public static void main(String[] args) throws ClassNotFoundException,
      		InstantiationException, IllegalAccessException {

      Class klass = Class.forName(args[0]);
      Object theNewObject = klass.newInstance();
      System.out.println("Just made: " + theNewObject);
   }
}

ประมวลผลโค้ดข้างบนพร้อมกับส่งค่า “com.russolsen.reflect.Employee” เข้าไปในโปรแกรม และคุณก็จะได้อ็อบเจกต์ใหม่เอี่ยมของ Employee ออกมา

Just made: Employee: John Smith 50000

ส่งประมวลผลอีกครั้ง พร้อมกับใส่ค่า “java.util.Date” แล้วคุณจะได้

Just made: Tue Feb 27 20:25:20 EST 2007

ลองนึกถึงความยืดหยุ่นของโปรแกรมที่คุณได้ จากเพียงแค่โค้ดไม่กี่บรรทัด โปรแกรมข้างบนนั้นแทบไม่มีข้อมูลอะไรเลยเกี่ยวกับ Employee หรือ Date แต่ก็ยังสามารถสร้างอินสแตนจากทั้งสองคลาสได้ นี่เป็นวิธีใช้จาวาอีกแบบหนึ่ง

มากกว่าคอนสตรัคเตอร์ที่ไม่มี argument
การเรียกเมท็อด Class.newInstance นั้นก็เหมือนกับการเรียกใช้ new แบบไม่ใส่ argument แต่จะเกิดอะไรขึ้นละ ถ้าคุณเรียก newInstance บนคลาสที่ไม่มีคอนสตรัคเตอร์ที่ไม่มี argument คำตอบคือคุณจะเจอกับ InstantiationException นะสิ
ข่าวดีก็คือคุณสามารถสร้างอินสแตนของคลาสที่มี constructor แบบรับ argument ได้ เพียงแค่ต้องทำงานเพิ่มอีกนิดหน่อย สิ่งที่ต้องทำคือ หาคอนสตรัคเตอร์ที่คุณต้องการของคลาสนั้น และเรียกใช้มันพร้อมกับ ส่งค่า argument ที่ถูกต้องเข้าไป วิธีสำหรับค้นหาคอนสตรัคเตอร์ในฝันของคุณก็คือ การเรียกเมท็อด getConstructor พร้อมกับข้อมูลอธิบายลักษณะคอนสตรัคเตอร์ ที่คุณกำลังค้นหา สิ่งที่คุณจะได้กลับมาคืออ็อบเจกต์ของคลาส Constructor ซึ่งคุณสามารถนำไปใช้สร้างอินสแตนใหม่ได้
มาดูกันว่าทั้งหมดนี้ทำงานอย่างไร

      Class klass = Class.forName("com.russolsen.reflect.Employee");

      Class[] paramTypes = {
            String.class, 
            String.class, 
            Integer.TYPE };
      
      Constructor cons = klass.getConstructor(paramTypes);
      
      System.out.println( "Found the constructor: " + cons);

      
      Object[] args = { 
            "Fred", 
            "Fintstone", 
            new Integer(90000) };
      
      Object theObject = cons.newInstance(args);
      System.out.println( "New object: " + theObject);

ความแตกต่างอย่างเดียว ระหว่างคอนสตรัคเตอร์แต่ละตัว ก็คือพารามิเตอร์ที่มันรับ ดังนั้นวิธีที่คุณจะบอก getConstructor ว่าคุณกำลังมองหาคอนสตรัคเตอร์ตัวไหนอยู่ ก็คือการส่งอาเรย์ของคลาสเข้าไป โดยแต่ละคลาสในอาเรย์ จะแทนแต่ละพารามิเตอร์ในคอนสตรัคเตอร์ ตัวอย่างข้างบนนั้น ทำการค้นหาคอนสตรัคเตอร์ ที่รับค่าสตริง 2 ตัวกับ int หนึ่งตัว เมื่อคุณได้คอนสตรัคเตอร์แล้ว การสร้างอ็อบเจกต์ใหม่นั้นก็แสนง่าย เพียงแค่เรียกเมท็อด newInstance โดยส่งอาเรย์อีกหนึ่งอันเข้าไป คราวนี้อาเรย์นั้นจะบรรจุค่าของ argument

มีข้อควรระวังอย่างหนึ่ง ในการกำหนดชนิดของพารามิเตอร์ สำหรับคอนสตรัคเตอร์ที่คุณกำลังค้นหาอยู่ คุณควรจะระมัดระวัง ในการแยกความแตกต่างระหว่าง argument ที่เป็น primitive กับอ็อบเจคที่เป็น wrapper ของมัน ต้องดูให้แน่ว่าคอนสตรัคเตอร์นั้นรับ argument ที่เป็น int หรือเป็นอ็อบเจกต์ของคลาส java.lang.Integer ถ้าคุณกำลังมองหาคอนสตรัคเตอร์ที่รับ wrapper ของชนิดข้อมูล primitive เช่น java.lang.Integer ก็ให้ใช้คลาสของ wrapper นั้นเลยเช่น Integer.class แต่ถ้าคุณต้องการคอนสตรัคเตอร์ที่รับค่า primitive int คุณจะต้องใช้ Integer.TYPE ซึ่งเป็นสิ่งที่ใช้แทนคลาสของ primitive int

ค้นลึกลงไปในคลาส
ดังที่คุณได้เห็นจากตัวอย่างแรก อ็อบเจกต์ของ Class สามารถให้ข้อมูลพื้นฐานทุกอย่าง เกี่ยวกับคลาสแก่คุณได้ ข้อมูลเช่นชื่อ หรือคลาสที่เป็นคลาสแม่ แต่คุณสามารถทำได้มากกว่า การเรียกดูข้อมูลในระดับพื้นฐานเช่นชื่อหรืออันดับ หรือตัวเลข serial เหล่านี้ ตัวอย่างเช่น คุณสามารถที่จะค้นหาเมท็อดที่เป็น public ทั้งหมดในคลาสโดยใช้เมท็อด getMethods

Class klass = Class.forName("com.russolsen.reflect.Employee");
           
Method[] methods = klass.getMethods();
      
for(Method m : methods ){
         System.out.println( "Found a method: " + m );
}

getMethods จะคืนกลับอาเรย์ของอ็อบเจกต์ของคลาส Method แต่ละอันคือหนึ่งเมท็อดที่เป็น public ที่สามารถเรียกใช้ได้บนอินสแตนของคลาสนั้น

Found a method: public java.lang.String com.russolsen.reflect.Employee.toString()
Found a method: public int com.russolsen.reflect.Employee.getSalary()
Found a method: public void com.russolsen.reflect.Employee.setSalary(int)
Found a method: public native int java.lang.Object.hashCode()
Found a method: public final native java.lang.Class java.lang.Object.getClass()
Found a method: public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
Found a method: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
Found a method: public final void java.lang.Object.wait() throws java.lang.InterruptedException
Found a method: public boolean java.lang.Object.equals(java.lang.Object)
Found a method: public java.lang.String java.lang.Object.toString()
Found a method: public final native void java.lang.Object.notify()
Found a method: public final native void java.lang.Object.notifyAll()

เพราะว่าเมท็อด getMethods นั้นมองคลาสในมุมมองของผู้ใช้งานคลาสนั้น ดังนั้นอาเรย์จะบรรจุเมท็อดที่เป็น public ทั้งหมดที่ถูกประกาศไว้ในคลาสของมันเอง รวมไปถึงคลาสแม่และคลาสเหนือคลาสแม่ขึ้นไปเรื่อยๆ จนถึงคลาส Object

ถ้าคุณสนใจเมท็อดอันใดอันหนึ่งโดยเฉพาะ คุณสามารถใช้ getMethod (โปรดสังเกตุว่าชื่อเมท็อดอยู่ในรูปเอกพจน์) ซึ่งทำงานคล้ายกับ getConstructor เว้นแต่ว่าคุณจะต้องใส่ชื่อเมท็อดพร้อมกับชนิดของพารามิเตอร์ โค้ดข้างล่างนี้ทำการค้นหาเมท็อดชื่อ setSalary ซึ่งรับพารามิเตอร์เป็น int หนึ่งตัว

Class klass = Class.forName("com.russolsen.reflect.Employee");
Class[] paramTypes = {Integer.TYPE };
Method setSalaryMethod = klass.getMethod("setSalary", paramTypes);
           
System.out.println( "Found method: " + setSalaryMethod);

การเรียกใช้เมท็อดผ่านทางรีเฟลคชันนั้น คล้ายกันมากกับการเรียกคอนสตรัคเตอร์ คุณต้องการเพียงแค่อ็อบเจกต์ของคลาส Method ที่เราได้พูดไปแล้วก่อนหน้านี้ รวมไปถึงอาเรย์ของ argument ที่จะส่งเข้าไปในเมท็อด และตัวอินแสตนจริงๆ ที่บรรจุเมท็อดที่คุณกำลังจะเรียก โค้ดข้างล่างนี้เรียกใช้เมท็อด setSalary บนอ็อบเจกต์ Employee เพื่อทำการเพิ่มเงินเดือน

     Class klass = Class.forName("com.russolsen.reflect.Employee");

      Class[] paramTypes = {Integer.TYPE };
      Method setSalaryMethod = klass.getMethod("setSalary", paramTypes);
      
      Object theObject = klass.newInstance();
      Object[] parameters = { new Integer(90000) };
      
      setSalaryMethod.invoke(theObject, parameters);

ทำไมต้องมาเสียเวลากับสิ่งเหล่านี้ เพื่อแลกกับการพิมพ์แค่ theObject.setSalary(90000) ละ อืม ลองดูโค้ดข้างบนอีกสักครั้ง นอกจากชื่อคลาสในบรรทัดแรกแล้ว โปรแกรมข้างบนนั้นมีความเป็น generic ในทั้งโปรแกรม คุณสามารถใช้มันเรียกเมท็อด setSalary ของอินสแตนใดๆบนคลาสใดก็ได้ ด้วยการปรับแต่งเล็กน้อย คุณก็จะสามารถใช้โค้ดข้างบนนั้น เรียกเมท็อดใดๆบนคลาสใดๆก็ได้

เล่นกับฟิลด์ (field)
คุณไม่ได้ถูกจำกัดให้ใช้รีเฟลคชันจัดการได้เฉพาะเมท็อด คุณยังสามารถทำอะไรก็ได้กับฟิลด์อีกด้วย เราได้เห็นการใช้งาน getMethods ไปแล้ว การใช้งาน getFields นั้นก็คล้ายๆกันคือจะส่งกลับอาเรย์ของ Field แต่ละอ็อบเจกต์ในอาเรย์นั้น ก็คือแต่ละอินสแตนฟิลด์ (instance field) ที่อยู่ในคลาสนั้น รวมไปถึงอินสแตนฟิลด์ที่ถูกประกาศในคลาสแม่ของมัน

Class klass = Class.forName("com.russolsen.reflect.Employee");

System.out.println( "Class name: " + klass.getName());      
Field[] fields = klass.getFields();
      
for(Field f : fields ){
    System.out.println( "Found field: " + f);
}

เนื่องจาก Employee มีฟิลด์แค่สองตัว ดังนั้นคุณจะได้อาเรย์ที่มีสมาชิกสองตัวกลับมา

Class name: com.russolsen.reflect.Employee
Found field: public java.lang.String com.russolsen.reflect.Employee._firstName
Found field: public java.lang.String com.russolsen.reflect.Employee._lastName

คุณสามารถที่เจาะจงดึงเอาฟิลด์อันใดอันหนึ่งมาด้วยชื่อโดยการใช้เมท็อด getField

Field field = klass.getField(“_firstName”);
System.out.println(“Found field: ” + field);

เมื่อคุณได้อ็อบเจกต์ของ Field แล้ว การดึงค่าของฟิลด์นั้นก็ง่ายพอๆกับการเรียกเมท็อด get และการกำหนดค่าให้กับฟิลด์นั้นก็เพียงแค่เรียกเมท็อด set

      Object theObject = new Employee("Tom", "Smith", 25);
      
      Class klass = Class.forName("com.russolsen.reflect.Employee");
      
      Field field = klass.getField("_firstName");
      
      Object oldValue = field.get(theObject);
      System.out.println( "Old first name is: " + oldValue);
      
      field.set( theObject, "Harry");
      Object newValue = field.get(theObject);
      System.out.println( "New first name is: " + newValue);

ส่งประมวลผลโค้ดข้างต้นแล้วมองดูฟิลด์ _firstName เปลี่ยนค่าไปให้เห็นกับตาคุณ

Old first name is: Tom
New first name is: Harry

แหกกฎ
การศึกษาเกี่ยวกับรีเฟลคชันนั้น จะครบถ้วนไปไม่ได้ หากไม่ได้พูดถึงการฝ่าฝืนกฎศักสิทธิ์อันหนึ่งของจาวาทุกคนคงรู้ดีว่าไพรเวทเมท็อด (private method) นั้นไม่สามารถเข้าถึงได้จากภายนอกคลาส ที่ประกาศมันไว้ วิธีการใช้งานทั่วไปนั้นไม่สามารถทำได้ แต่กับรีเฟลคชันแล้ว คุณแทบจะทำอะไรได้ทุกอย่าง

สิ่งแรกที่คุณต้องการในการเรียกใช้ไพรเวทเมท็อดก็คืออ็อบเจกต์ Method ของเมท็อดที่คุณต้องการเรียก คุณไม่สามารถดึงมันออกมาได้จากการเรียก getMethod เพราะ getMethod จะส่งกลับเฉพาะเมท็อดที่เป็น public เท่านั้น วิธีที่คุณจะได้มาซึ่งไพรเวทเมท็อด(หรือ protected) ก็คือการใช้ getDeclaredMethod ในขณะที่ getMethod มองคลาสในมุมมองของผู้ใช้งาน และส่งกลับเฉพาะเมท็อดที่เป็น public แต่ getDeclaredMethod จะส่งกลับทุกเมท็อดที่ถูกประกาศไว้ในคลาสนั้น ตัวอย่างข้างล่างเราใช้ getDeclaredMethod ในการดึงไพรเวทเมท็อด removeRange ของคลาส java.util.ArrayList

ArrayList list = new ArrayList();
list.add("Larry");
list.add("Moe");
list.add("Curley");

System.out.println("The list is: " + list);

Class klass = list.getClass();

Class[] paramTypes = { Integer.TYPE, Integer.TYPE };
Method m = klass.getDeclaredMethod("removeRange", paramTypes);

Object[] arguments = { new Integer(0), new Integer(2) };
m.setAccessible(true);
m.invoke(list, arguments);
System.out.println("The new list is: " + list);

เมื่อคุณได้ไพรเวทเมท็อดมาแล้ว การจะเรียกใช้มันได้ก็เพียงแค่ปิดการป้องกันสุดท้ายโดยการเรียก setAccessable

The list is: [Larry, Moe, Curley]
The new list is: [Curley]

เมท็อด removeRange จะทำการดึงเอาไอเทมในช่วงหนึ่งออกจากลิสต์ นี่อาจจะออกแนวไสยศาสตร์วูดูเลยทีเดียว คุณพึ่งจะเข้าไปเรียกไพรเวทเมท็อดของ java.util class แต่ถามว่าคุณอยากจะสร้างนิสัยในการเขียนโค้ดที่คลุมเครือ อย่างการเรียกใช้งานไพรเวทเมท็อดแบบนี้ไหมละ ก็คงไม่หรอก แต่อย่างน้อยมันก็เป็นสิ่งที่รู้กัน ว่ามีประโยชน์ ในบางสถาณการณ์ที่จำเป็นจริงๆ

สรุป
รีเฟลคชันอนุญาตให้โปรแกรมของคุณ ทำในสิ่งที่เหมือนจะไม่เป็นไปตากกฎของจาวา คุณสามารถเขียนโค้ด เพื่อหาข้อมูลทุกอย่างของคลาสหนึ่งๆ ที่โค้ดของคุณเพิ่งเจอเป็นครั้งแรก และเรียกใช้งานข้อมูลที่เพิ่งจะค้นพบในคลาสใหม่นั้นได้ทันที เช่นมันสามารถสร้างอินสแตนไหม่ หรือเรียกใช้เมท็อด และดึงค่าหรือกำหนดค่าให้กับฟิลด์ คุณสามารถทำได้แม้กระทั่ง เล่นเล่นกับข้อมูลที่เป็นไพรเวทข้างในคลาส ความเข้าใจที่ดีในรีเฟลคชันเป็นสิ่งที่สำคัญในการทำความเข้าใจ เครื่องมือที่ซับซ้อนบางอย่าง ในโลกของจาวา และมันก็เป็นสิ่งที่คุณต้องการ ในการเขียนโปรแกรมที่ทำได้มากกว่าโปรแกรมจาวาทั่วไป