บทความนี้มีวัตถุประสงค์เพื่อให้การแนะนำที่ครอบคลุมเกี่ยวกับกลไกการสะท้อนชวา ฉันหวังว่าผ่านบทความนี้คุณจะมีความเข้าใจที่ครอบคลุมเกี่ยวกับเนื้อหาที่เกี่ยวข้องของการสะท้อนชวา
ก่อนที่จะอ่านบทความนี้คุณสามารถอ้างถึง " การเข้าใจ Java Generics " อีกครั้ง "
คำนำ
กลไกการสะท้อน Java เป็นฟังก์ชั่นที่ทรงพลังมาก การสะท้อนสามารถเห็นได้ในโครงการขนาดใหญ่หลายโครงการเช่นฤดูใบไม้ผลิและ mybatis ผ่านกลไกการสะท้อนกลับเราสามารถรับข้อมูลประเภทวัตถุในระหว่างการทำงาน การใช้คุณสมบัตินี้เราสามารถใช้รูปแบบการออกแบบเช่นโหมดโรงงานและโหมดพร็อกซีและยังสามารถแก้ปัญหาที่น่าวิตกเช่นการกำจัดจาวาทั่วไป ในบทความนี้เราจะใช้กลไกการสะท้อน Java จากมุมมองของการใช้งานจริง
พื้นฐานการไตร่ตรอง
PS: บทความนี้ต้องการให้ผู้อ่านมีความเข้าใจในระดับหนึ่งของกลไกการสะท้อน API หากคุณยังไม่เคยสัมผัสมาก่อนขอแนะนำให้ดูการเริ่มต้นเอกสารอย่างเป็นทางการก่อน
ก่อนที่จะใช้กลไกการสะท้อนกลับก่อนอื่นให้ดูที่วิธีการรับ Class การสะท้อนที่สอดคล้องกับวัตถุ ใน Java เรามีสามวิธีในการรับคลาสการสะท้อนของวัตถุ
โดยวิธี getClass
ใน Java แต่ละ Object มีวิธี getClass ด้วยวิธี getClass เราสามารถรับคลาสการสะท้อนที่สอดคล้องกันของวัตถุนี้:
String S = "Ziwenxie"; คลาส <?> c = s.getClass ();
นอกจากนี้เรายังสามารถเรียกใช้วิธีการคง forName ของ Class :
คลาส <?> c = class.forName ("java.lang.string"); หรือเราสามารถใช้ .class โดยตรง:
คลาส <?> c = string.class;
ในตอนต้นของบทความเรากล่าวว่าหนึ่งในประโยชน์ที่สำคัญของการสะท้อนคือมันช่วยให้เราได้รับข้อมูลประเภทวัตถุในระหว่างการดำเนินการ ลองดูตัวอย่างด้วยตัวอย่าง
ก่อนอื่นเราสร้างอินเทอร์เฟ A ใหม่ภายใต้แพ็คเกจ typeinfo.interfacea :
แพ็คเกจ typeinfo.interfacea; อินเตอร์เฟสสาธารณะ a {void f (); - จากนั้นเราสร้างอินเตอร์เฟสใหม่ C ภายใต้แพ็คเกจ typeinfo.packageaccess อินเทอร์เฟซ C สืบทอดมาจากอินเตอร์เฟส A และเรายังสร้างวิธีการอื่น ๆ อีกมากมายสำหรับการทดสอบ โปรดทราบว่าการอนุญาตของวิธีการต่อไปนี้แตกต่างกัน
แพ็คเกจ typeinfo.packageaccess; นำเข้า typeinfo.interfacea.a; คลาส C ใช้ {โมฆะสาธารณะ f () {system.out.println ("public cf ()"); } โมฆะสาธารณะ g () {system.out.println ("public cg ()"); } void void v () {system.out.println ("ป้องกัน cv ()"); } void u () {system.out.println ("แพ็คเกจ cu ()"); } โมฆะส่วนตัว w () {system.out.println ("ส่วนตัว cw ()"); }} คลาสสาธารณะ HIDDENC {สาธารณะคงที่ a makea () {return ใหม่ c (); - ในเมธอด callHiddenMethod() เราใช้ API ใหม่หลายตัวโดยที่ getDeclaredMethod() ใช้เพื่อรับวิธีการที่คลาสคลาสหมายถึงวัตถุตามชื่อวิธีการแล้วเราสามารถเรียกใช้วิธีการที่เกี่ยวข้องของวัตถุโดยเรียกใช้ invoke() : วิธีการ:
แพ็คเกจ typeinfo; นำเข้า typeinfo.interfacea.a; นำเข้า typeinfo.packageaccess.hiddenc; นำเข้า java.lang.reflect.method; ชั้นสาธารณะ hiddenimplementation AF (); System.out.println (A.getClass (). getName ()); // อ๊ะ! การสะท้อนยังคงช่วยให้เราสามารถเรียก g (): callhiddenmethod (a, "g"); // และแม้แต่วิธีการที่เข้าถึงได้น้อยกว่า! CallHiddenMethod (a, "u"); CallHiddenMethod (A, "V"); CallHiddenMethod (A, "W"); } โมฆะคงที่ callHiddenMethod (วัตถุ A, String MethodName) พ่นข้อยกเว้น {วิธี g = a.getClass (). getDeclaredMethod (MethodName); G.SetAccessible (จริง); G.Invoke (A); - จากผลลัพธ์ผลลัพธ์เราจะเห็นได้ว่าไม่ว่าจะเป็น public , default , protect หรือวิธีการ pricate เราสามารถเรียกได้อย่างอิสระผ่านคลาสการสะท้อนกลับ แน่นอนว่าเราเพียงแค่แสดงพลังอันทรงพลังของการไตร่ตรองและไม่แนะนำให้ใช้เทคนิคนี้ในการพัฒนาจริง
public cf () typeInfo.packageaccess.cpublic cg () แพ็คเกจ cu () ป้องกัน cv () ส่วนตัว CW () ส่วนตัว CW ()
เรามีสถานการณ์ทางธุรกิจต่อไปนี้ เรามี List<Class<? extends Pet>> เราจำเป็นต้องนับจำนวน Pet ที่เฉพาะเจาะจงในคลาสคอลเลกชันนี้ เนื่องจากการลบออกจาวาทั่วไปจึงเป็นไปไม่ได้ที่จะให้ความสนใจกับการฝึกฝนคล้ายกับ List<? extends Pet> เนื่องจากหลังจากคอมไพเลอร์ตรวจสอบประเภทคงที่ JVM จะปฏิบัติต่อวัตถุทั้งหมดในคอลเลกชันเป็น Pet ในระหว่างการวิ่ง แต่จะไม่ทราบว่า Pet หมายถึง Cat หรือ Dog ดังนั้นข้อมูลประเภทของวัตถุจะหายไปในระหว่างการวิ่ง PS: เกี่ยวกับการลบทั่วไป: ฉันมีคำอธิบายโดยละเอียดในบทความก่อนหน้า เพื่อนที่สนใจสามารถดูได้
ในการใช้ตัวอย่างของเราข้างต้นก่อนอื่นเราจะกำหนดชั้นเรียนหลายคลาส:
สัตว์เลี้ยงระดับสาธารณะขยายส่วนบุคคล {สัตว์เลี้ยงสาธารณะ (ชื่อสตริง) {super (ชื่อ); } สัตว์เลี้ยงสาธารณะ () {super (); }} cat คลาสสาธารณะขยายสัตว์เลี้ยง {แมวสาธารณะ (ชื่อสตริง) {super (ชื่อ); } แมวสาธารณะ () {super (); }} สุนัขระดับสาธารณะขยายสัตว์เลี้ยง {สุนัขสาธารณะ (ชื่อสตริง) {super (ชื่อ); }} คลาสสาธารณะ EgyptianMau ขยาย Cat {Public EgyptianMau (ชื่อสตริง) {Super (ชื่อ); } Public Public EgyptianMau () {super (); }} mutt คลาสสาธารณะขยายสุนัข {public mutt (ชื่อสตริง) {super (ชื่อ); } public mutt () {super (); - คลาส Pet ด้านบนสืบทอดมาจาก Individual การดำเนินการของ Individual ชั้นเรียนนั้นซับซ้อนกว่าเล็กน้อย เราใช้อินเทอร์เฟซ Comparable และกำหนดกฎการเปรียบเทียบคลาสใหม่ หากเราไม่เข้าใจมันเป็นอย่างดีมันก็ไม่สำคัญ เราได้สรุปแล้วดังนั้นจึงไม่สำคัญว่าเราจะไม่เข้าใจหลักการการนำไปปฏิบัติ
การใช้งานระดับบุคคลที่เทียบเท่ากับแต่ละบุคคล <bersonal> {ส่วนตัวคงที่ความยาวคงที่ = 0; ID Long Final Private Final = Counter ++; ชื่อสตริงส่วนตัว; // ชื่อเป็นตัวเลือกสาธารณะบุคคล (ชื่อสตริง) {this.name = name; } บุคคลสาธารณะ () {} สตริงสาธารณะ toString () {return getClass (). getSimplename () + (ชื่อ == null? "": "" + ชื่อ); } Public Long ID () {return id; } บูลีนสาธารณะเท่ากับ (วัตถุ o) {return o อินสแตนซ์ของบุคคล && id == ((บุคคล) o) .id; } public int hashCode () {int result = 17; if (name! = null) {result = 37 * result + name.hashCode (); } result = 37 * ผลลัพธ์ + (int) id; ผลการกลับมา; } public int compereto (แต่ละ arg) {// เปรียบเทียบโดยชื่อคลาสก่อน: string first = getClass (). getSimplename (); String argfirst = arg.getClass (). getSimplename (); int firstCompare = first.Compareto (argfirst); if (firstCompare! = 0) {return firstCompare; } if (name! = null && arg.name! = null) {int secondaryCompare = name.Compareto (arg.name); if (secendCompare! = 0) {ส่งคืน secondaryCompare; }} return (arg.id <id? -1: (arg.id == id? 0: 1)); - ด้านล่างเป็น PetCreator ระดับนามธรรม ในอนาคตเราสามารถรับคอลเลกชันของคลาส Pet ที่เกี่ยวข้องโดยตรงโดยเรียกวิธี arrayList() ที่นี่เราใช้เมธอด newInstance() ที่เราไม่ได้กล่าวถึงข้างต้น มันจะส่งคืนอินสแตนซ์ของคลาสที่คลาสอ้างอิงจริงๆ สิ่งนี้หมายความว่าอย่างไร? ตัวอย่างเช่นการประกาศ new Dog().getClass().newInstance() และ Direct new Dog() มีค่าเทียบเท่า
PetCreator ระดับนามธรรมสาธารณะ {RAND RAND ส่วนตัว = ใหม่สุ่ม (47); // รายการของ gettypes ที่แตกต่างกันของสัตว์เลี้ยงเพื่อสร้าง: รายการนามธรรมสาธารณะ <คลาส <? ขยายสัตว์เลี้ยง >> getTypes (); สัตว์เลี้ยงสาธารณะ Randompet () {// สร้างสัตว์เลี้ยงแบบสุ่มหนึ่งตัว int n = rand.nextint (getTypes (). size ()); ลอง {return gettypes (). get (n) .newinstance (); } catch (InstantiationException e) {โยน runtimeException ใหม่ (E); } catch (unglegalAccessException e) {โยน runtimeException ใหม่ (e); }} สัตว์เลี้ยงสาธารณะ [] createarray (ขนาด int) {pet [] result = สัตว์เลี้ยงใหม่ [ขนาด]; สำหรับ (int i = 0; i <size; i ++) {ผลลัพธ์ [i] = randompet (); } ผลตอบแทนผลลัพธ์; } arrayList สาธารณะ <et> arrayList (ขนาด int) {arrayList <et> result = new ArrayList <et> (); collections.addall (ผลลัพธ์, createarray (ขนาด)); ผลการกลับมา; - ถัดไปเรามาใช้คลาสนามธรรมข้างต้นและอธิบายรหัสต่อไปนี้ ในรหัสต่อไปนี้เราประกาศสองคลาสคอลเลกชัน allTypes และ types ซึ่ง allTypes มีคลาสทั้งหมดที่ประกาศไว้ข้างต้น แต่ประเภทเฉพาะของเรามีเพียงสองประเภทเท่านั้นคือ Mutt และ EgypianMau ดังนั้นสัตว์เลี้ยงที่เราต้องได้รับ new เป็นเพียงประเภทที่มีอยู่ใน types ในอนาคตเราจะได้รับประเภทที่อยู่ใน types โดยการเรียก getTypes()
Public Class LaypetCreator ขยาย PetCreator {@SuppressWarnings ("ไม่ได้ตรวจสอบ") รายการสุดท้ายคงที่สาธารณะ <คลาส <? ขยายสัตว์เลี้ยง >> allTypes = collections.unmodifiableList (array.aslist (pet.class, dog.class, cat.class, mutt.class, อียิปต์ mau.class); รายการสุดท้ายคงที่ส่วนตัว <คลาส <? ขยายสัตว์เลี้ยง >> ประเภท = AllTypes.Sublist (AllTypes.indexof (Mutt.Class), AllTypes.Size ()); รายการสาธารณะ <คลาส <? ขยายสัตว์เลี้ยง >> getTypes () {ประเภทการส่งคืน; - ตรรกะโดยรวมเสร็จสมบูรณ์และในที่สุดเราก็ใช้คลาส TypeCounter ที่ใช้เพื่อนับจำนวนคลาส Pet ที่เกี่ยวข้องในชุด อธิบายวิธี isAssignalbeFrom() ซึ่งสามารถกำหนดได้ว่าคลาสการสะท้อนกลับเป็นคลาสย่อยหรือคลาสย่อยทางอ้อมของคลาสการสะท้อน ตามชื่อแนะนำ getSuperclass() คือการได้คลาสแม่ของคลาสการสะท้อน
Typecounter คลาสสาธารณะขยาย HASHMAP <class <?>, Integer> {คลาสส่วนตัว <?> basetype; typecounter สาธารณะ (คลาส <?> basetype) {this.basetype = basetype; } การนับโมฆะสาธารณะ (Object obj) {class <?> type = obj.getClass (); if (! basetype.isassignableFrom (ประเภท)) {โยน runtimeException ใหม่ (obj + "ประเภทที่ไม่ถูกต้อง" + type + "ควรพิมพ์หรือชนิดย่อยของ" + basetype); } countClass (ประเภท); } โมฆะส่วนตัว CountClass (คลาส <?> type) {ปริมาณจำนวนเต็ม = get (type); ใส่ (พิมพ์ปริมาณ == null? 1: ปริมาณ + 1); คลาส <?> superClass = type.getSuperClass (); if (superclass! = null && basetype.isassignablefrom (superclass)) {countclass (superclass); }} @Override Public String ToString () {StringBuilder result = new StringBuilder ("{"); สำหรับ (map.entry <class <?>, integer> pair: entryset ()) {result.append (pair.getKey (). getSimplename ()); result.append ("="); result.append (pair.getValue ()); result.append (","); } result.delete (result.length () - 2, result.length ()); result.append ("}"); Return result.toString (); -สรุป
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้เกี่ยวกับการแบ่งปันรหัสตัวอย่างของกลไกการสะท้อน Java และฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน เพื่อนที่สนใจสามารถอ้างถึงเว็บไซต์นี้ต่อไปได้:
รหัสการใช้งานการเขียนโปรแกรม Java และการพิมพ์ใบเสร็จรับเงิน
คำอธิบายโดยละเอียดเกี่ยวกับการดำเนินการอ้างอิงและพร็อกซีแบบไดนามิกใน Java
การเขียนโปรแกรม Java เพื่อใช้การแบ่งปันรหัสอย่างง่ายของ Lunar Eclipse
หากมีข้อบกพร่องใด ๆ โปรดฝากข้อความไว้เพื่อชี้ให้เห็น ขอบคุณเพื่อนที่ให้การสนับสนุนเว็บไซต์นี้!