제이온

[Java] แนวคิดและวิธีการใช้งาน Reflection

สร้าง: 2024-04-25

สร้าง: 2024-04-25 22:27

การสะท้อน (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

สร้างเฟรมเวิร์กระดับ DI ด้วย Node.js แม้จะเป็นมือใหม่บทความนี้จะอธิบายวิธีการใช้การอัดฉีดการพึ่งพา (DI) ในการพัฒนาเซิร์ฟเวอร์ Node.js โดยใช้ฟังก์ชัน Reflect Metadata ของคอมไพเลอร์ TypeScript ในการใช้ DI และแนะนำไลบรารีที่เกี่ยวข้อง
Sunrabbit
Sunrabbit
Sunrabbit
Sunrabbit

November 8, 2024

[สำหรับผู้ไม่ใช่ผู้เชี่ยวชาญ ด้านการพัฒนาซอฟต์แวร์ เพื่อความอยู่รอด] 14. สรุปเนื้อหาสัมภาษณ์ทางเทคนิคที่ผู้พัฒนาซอฟต์แวร์มือใหม่ถามบ่อยสรุปคำถามทางเทคนิคที่มักถามในการสัมภาษณ์งานผู้พัฒนาซอฟต์แวร์มือใหม่ (พื้นที่หน่วยความจำ โครงสร้างข้อมูล ฐานข้อมูล ฯลฯ) หวังว่าจะเป็นประโยชน์ในการเตรียมตัวสัมภาษณ์งานด้านการพัฒนา
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

April 3, 2024

LLM สำหรับเด็กประถมคำอธิบายแนวคิด LLM ที่แม้แต่เด็กประถมก็เข้าใจ! LLM คือ AI ที่ตอบคำถามเป็นข้อความเมื่อได้รับคำถามเป็นข้อความ สามารถทำงานได้หลากหลาย เช่น การเขียนโค้ด การวิเคราะห์ภาพ ฯลฯ ปัจจุบันนักพัฒนาใช้ AI เป็นเครื่องมือ
Sunrabbit
Sunrabbit
Sunrabbit
Sunrabbit

March 4, 2025

29 พ.ย. 2567 ข่าวล่าช้า: สมัคร Replit แบบชำระเงิน / นิสัยการพัฒนาซอฟต์แวร์ที่ดีบทความบล็อกที่เขียนเมื่อวันที่ 29 พฤศจิกายน พ.ศ. 2567 นี้จะแบ่งปันรีวิวการสมัครสมาชิกแบบชำระเงินของ Replit และแนะนำนิสัยการพัฒนาซอฟต์แวร์ที่ดี 10 ประการ ครอบคลุมกลยุทธ์การเขียนโค้ดและการปรับโครงสร้างที่มีประสิทธิภาพ รวมถึงความสำคัญของการทดสอบ
Charles Lee
Charles Lee
Charles Lee
Charles Lee

November 29, 2024

ประสบการณ์การมีส่วนร่วมโอเพนซอร์สบทความนี้แบ่งปันประสบการณ์การมีส่วนร่วมโอเพนซอร์ส ช่วยสร้างแรงบันดาลใจให้เอาชนะความกลัวและก้าวแรก เริ่มต้นจากการปรับปรุงเล็กๆ น้อยๆ แล้วค่อยๆ สร้างความมั่นใจและเติบโต
seungwon
seungwon
seungwon
seungwon

May 3, 2025

สร้าง AI Full Stack ด้วยโอเพนซอร์สบทความนี้จะแนะนำเครื่องมือต่างๆ สำหรับสร้าง AI 풀สแตก (Full Stack) ด้วยโอเพนซอร์ส รวมถึง LLM, เครื่องมืออนุมานและให้บริการ, เฟรมเวิร์ก และโซลูชันการตรวจสอบต่างๆ เรียนรู้การพัฒนาแอปพลิเคชัน AI โดยใช้ LangChain, LlamaIndex และอื่นๆ
RevFactory
RevFactory
RevFactory
RevFactory

February 5, 2024