การสะท้อน (Reflection) คืออะไร?
การสะท้อน (Reflection) คือ API ที่ช่วยให้สามารถสร้างอินสแตนซ์ของคลาสที่ต้องการได้ผ่านทางออบเจ็กต์ประเภท Class ที่โหลดลงในพื้นที่ Heap และสามารถเข้าถึงฟิลด์และเมธอดของอินสแตนซ์ได้โดยไม่คำนึงถึงตัวควบคุมการเข้าถึง (Access Modifier)
ที่นี่ คลาสที่โหลดแล้ว หมายถึงหลังจากที่ Class Loader ของ JVM ทำการโหลดไฟล์คลาสเสร็จสิ้นแล้ว จะสร้าง ออบเจ็กต์ประเภท Class ที่เก็บข้อมูลของคลาสดังกล่าวไว้ในพื้นที่ Heap ของหน่วยความจำ โปรดทราบว่าแตกต่างจากออบเจ็กต์ที่สร้างด้วยคีย์เวิร์ด new หากคุณยังไม่เข้าใจออบเจ็กต์ประเภท Class นี้ คุณสามารถดูเอกสาร JDK ของ java.lang.class ได้
วิธีการใช้งาน
ก่อนที่จะใช้การสะท้อน (Reflection) คุณต้องดึงออบเจ็กต์ประเภท Class ที่โหลดลงในพื้นที่ Heap ก่อน มีทั้งหมด 3 วิธี
- ดึงด้วย Class.class
- ดึงด้วย Instance.getClass()
- ดึงด้วย Class.forName("ชื่อคลาส")
สามารถตรวจสอบได้ว่าอินสแตนซ์ประเภท Class ที่ดึงมาด้วย 3 วิธีนี้เหมือนกันทั้งหมด เนื่องจากค่าแฮชเหมือนกัน ดังนั้นจึงสามารถใช้วิธีใดวิธีหนึ่งได้ตามความเหมาะสม
ตอนนี้ ด้วย Class ประเภทที่ดึงมา เราสามารถสร้างอินสแตนซ์ของคลาสดังกล่าวได้ และสามารถเข้าถึงฟิลด์และเมธอดของอินสแตนซ์ได้โดยไม่คำนึงถึงตัวควบคุมการเข้าถึง ลองสร้างอินสแตนซ์ของคลาสนี้ก่อน
สามารถใช้ getConstructor() เพื่อรับ Constructor และใช้ newInstance() เพื่อสร้างอินสแตนซ์ Member แบบไดนามิกได้
สุดท้าย ลองเข้าถึงฟิลด์และเมธอดของอินสแตนซ์โดยไม่คำนึงถึงตัวควบคุมการเข้าถึง
สามารถใช้ getDeclaredFileds() เพื่อรับตัวแปรอินสแตนซ์ทั้งหมดของคลาส และใช้ get() เพื่อรับค่าฟิลด์ และใช้ set() เพื่อแก้ไขค่าฟิลด์ สิ่งที่ควรทราบคือเมื่อเข้าถึงฟิลด์ที่มีตัวควบคุมการเข้าถึงเป็น private คุณต้องส่งค่า true เป็นพารามิเตอร์ของ setAccessible()
เมธอดก็สามารถรับได้ด้วย getDeclaredMethod() เช่นกัน ในกรณีนี้ คุณต้องส่งชื่อเมธอดและชนิดของพารามิเตอร์เป็นพารามิเตอร์ด้วยเช่นกัน ในทำนองเดียวกัน เมื่อเข้าถึงเมธอดที่มีตัวควบคุมการเข้าถึงเป็น private คุณต้องตั้งค่าพารามิเตอร์ของ setAccessible() เป็น true สุดท้าย คุณสามารถเรียกใช้เมธอดที่ได้รับจาก API การสะท้อน (Reflection) ด้วย invoke()
ข้อดีข้อเสีย
- ข้อดี
- มีความยืดหยุ่น สามารถสร้างอินสแตนซ์ของคลาสและเข้าถึงฟิลด์และเมธอดได้โดยไม่คำนึงถึงตัวควบคุมการเข้าถึงในขณะรันไทม์ เพื่อดำเนินการที่จำเป็น
- ข้อเสีย
- ละเมิดการห่อหุ้ม (Encapsulation)
- เนื่องจากสร้างอินสแตนซ์ในขณะรันไทม์ จึงไม่สามารถตรวจสอบชนิดของข้อมูลในขณะคอมไพล์ได้
- เนื่องจากสร้างอินสแตนซ์ในขณะรันไทม์ จึงยากที่จะทำความเข้าใจลำดับการทำงานที่เฉพาะเจาะจง
- ประสิทธิภาพในการเข้าถึงฟิลด์และเมธอดโดยใช้การสะท้อน (Reflection) ช้ากว่าการเข้าถึงโดยตรง (แต่ไม่ใช่ในทุกสถานการณ์)
เหตุผลในการใช้งาน
ผ่านทาง API การสะท้อน (Reflection) เราสามารถเข้าถึงข้อมูลของคลาสและจัดการคลาสได้ตามต้องการในขณะรันไทม์ รวมถึงฟิลด์และเมธอดที่ประกาศด้วยตัวควบคุมการเข้าถึงเป็น private ด้วย ดูเหมือนว่าจะเป็นเทคนิคที่ไม่ควรใช้เนื่องจากทำลายการห่อหุ้ม (Encapsulation) ซึ่งเป็นสิ่งสำคัญในงานออกแบบเชิงวัตถุ
ในระดับคอนโซลที่มีขนาดเล็ก นักพัฒนาสามารถระบุออบเจ็กต์และความสัมพันธ์ของการพึ่งพาที่ใช้ในโปรแกรมได้ในขณะคอมไพล์ แต่ในขั้นตอนการพัฒนาที่มีขนาดใหญ่ เช่น เฟรมเวิร์กนั้น ยากที่จะระบุออบเจ็กต์และความสัมพันธ์ของการพึ่งพามากมาย ในกรณีนี้ การใช้การสะท้อน (Reflection) ช่วยให้สามารถสร้างคลาสและสร้างความสัมพันธ์ของการพึ่งพาแบบไดนามิกได้
ตัวอย่างเช่น ใน Spring Bean Factory หากเราใส่ Annotations เช่น @Controller, @Service, @Repository ลงไป Bean Factory จะสร้างและจัดการคลาสที่มี Annotations เหล่านี้โดยอัตโนมัติ นักพัฒนาไม่ได้แจ้งให้ Bean Factory ทราบถึงคลาสเหล่านี้ แต่ทำไมถึงเป็นไปได้ นั่นก็เพราะการสะท้อน (Reflection) นั่นเอง ในขณะรันไทม์ จะค้นหาและตรวจสอบคลาสที่มี Annotations เหล่านี้ และหากพบ ก็จะใช้การสะท้อน (Reflection) สร้างอินสแตนซ์ของคลาสเหล่านั้น และใส่ฟิลด์ที่จำเป็นลงไป จากนั้นจึงบันทึกไว้ใน Bean Factory
แน่นอนว่า ดังที่ได้กล่าวไปแล้ว การละเมิดการห่อหุ้ม (Encapsulation) จึงควรใช้ในกรณีที่จำเป็นเท่านั้น
ความคิดเห็น0