คำนำ
เมื่อเร็ว ๆ นี้ในระหว่างกระบวนการเรียนรู้ฉันค้นพบปัญหา คลาสที่เป็นนามธรรมไม่สามารถสร้างวัตถุผ่านใหม่โดยไม่ต้องใช้วิธีนามธรรมทั้งหมด แต่วิธีการนามธรรมสามารถมีวิธีการก่อสร้างของตัวเอง สิ่งนี้ทำให้ฉันสับสน เนื่องจากมีวิธีการก่อสร้างและไม่สามารถสร้างได้ผ่านใหม่คลาสนามธรรมสามารถสร้างอินสแตนซ์เมื่อพวกเขาไม่ได้กลายเป็นคลาสคอนกรีต?
ใน Java คลาสนามธรรมไม่สามารถสร้างอินสแตนซ์ได้โดยตรง อย่างไรก็ตามคุณสมบัติของคลาสนามธรรมนี้มักจะกลายเป็นอุปสรรคที่ลำบาก ตัวอย่างเช่นหากฉันต้องการใช้พร็อกซีแบบไดนามิกเพื่อให้คลาสนามธรรมมีความสามารถในการดำเนินการวิธีการนามธรรมจะมีสองปัญหา: 1. พร็อกซีแบบไดนามิกสามารถสร้างวัตถุพร็อกซีที่ใช้อินเทอร์เฟซ แต่ไม่ใช่วัตถุที่สืบทอดคลาสนามธรรม สำหรับ JVM มาตรฐานนี้มีการใช้งานบางอย่างเช่น Javassist ซึ่งสามารถใช้เพื่อให้สำเร็จโดยใช้เครื่องมือ bytecode (ProxyFactory)
หากคุณต้องการสร้างวัตถุคลาสนามธรรมใน Android คุณอาจมี new ClassName() {} หรือสืบทอด อย่างไรก็ตามทั้งสองวิธีไม่สามารถดำเนินการโดยตรงโดยวัตถุคลาสของพวกเขาซึ่งนำไปสู่ปัญหาบางอย่างที่ไม่สามารถบรรลุความสามารถเชิงนามธรรมที่เราต้องการ
นี่คือคำอธิบายโดยละเอียดของฉากที่กล่าวถึงในย่อหน้าแรก:
ก่อนอื่นมีไฟล์อินเทอร์เฟซที่กำหนดดังนี้ (เพื่อนที่คุ้นเคยกับ Android สามารถเห็นได้ว่านี่เป็นอินเตอร์เฟสการกำหนดค่า API ที่มีให้สำหรับการติดตั้งเพิ่มเติมเพื่อสร้างวัตถุพร็อกซี):
อินเทอร์เฟซสาธารณะ realapi {@get ("api1") สังเกตได้ <string> api1 (); @get ("api2") สังเกตได้ <string> api2 (); @get ("api3") สังเกตได้ <string> api3 (); //....Thother Methods}ถัดไปเขียนคลาสนามธรรมเพื่อใช้เพียงวิธีเดียวของอินเทอร์เฟซ (ใช้เพื่อจำลองข้อมูลอินเตอร์เฟส):
@mockapipubublic คลาสบทคัดย่อ Mockapi ใช้ realapi {สังเกตได้ <string> api3 () {return newobable.just ("ข้อมูลเยาะเย้ย"); -จากนั้นเราต้องมีเครื่องมือเช่น MockManager ที่รวมวัตถุ Realapi ที่มีอยู่ของเราและคลาส Mockapi เพื่อสร้างวัตถุผสม เมื่อดำเนินการวิธีการที่กำหนดไว้แล้วใน mockapi มันจะดำเนินการโดยตรง เมื่อ Mockapi ไม่ได้กำหนดวิธีการมันจะเรียกวิธี Realapi วิธีการโทรประมาณ:
realapi api = mockmanager.build (realapi, mockapi.class);
ผ่าน Javassist มันง่ายมากที่จะทำฟังก์ชั่นข้างต้นให้สมบูรณ์ สร้างวัตถุ ProxyFactory ตั้งค่า superclass เป็น mockapi จากนั้นกรองวิธีนามธรรมและตั้งค่าตัวจัดการเมธอดเพื่อเรียกวัตถุ Realapi ด้วยชื่อและวิธีพารามิเตอร์เดียวกัน การใช้งานรหัสจะไม่ได้รับที่นี่
แต่บน Android วิธีการของ Javassist จะถูกโยนลงไป
เกิดจาก: java.lang.unsupportedOperationException: ไม่สามารถโหลดไฟล์คลาสประเภทนี้ได้ที่ java.lang.classloader.defineclass (classloader.java:520) ที่ java.lang.reflect.method.invoke javassist.util.proxy.factoryhelper.toclass2 (FactoryHelper.java:182)
ข้อยกเว้นที่คล้ายกัน เหตุผลอาจเป็นไปได้ว่าการใช้งานและมาตรฐานของเครื่องเสมือนจริงบน Android นั้นแตกต่างกันเล็กน้อยดังนั้นที่นี่เราเปลี่ยนทิศทางไปอีกทิศทางหนึ่งของโปรเซสเซอร์การเพิ่มรหัสการสร้างรหัสแบบไดนามิก
หากคุณใช้ตัวประมวลผลคำอธิบายประกอบเพื่อนำไปใช้แนวคิดนั้นง่ายกว่ามาก แต่กระบวนการยังคงคดเคี้ยวเล็กน้อย:
ก่อนกำหนดคำอธิบายประกอบเพื่อทำเครื่องหมายคลาสนามธรรมที่ต้องสร้าง
@Target (elementType.type)@documented@retention (RetentionPolicy.Source) สาธารณะ @Interface Mockapi {} โปรเซสเซอร์ได้รับวัตถุองค์ประกอบของคลาสตามคำอธิบายประกอบซึ่งเป็นวัตถุเหมือนคลาส เนื่องจากชั้นเรียนยังไม่มีอยู่ในขั้นตอนการคอมไพล์ precompilation จึงเป็นไปไม่ได้ที่จะได้รับวัตถุคลาสที่ต้องการโดยรันไทม์โดยใช้ Class.forName อย่างไรก็ตามองค์ประกอบมีวิธีการที่คล้ายกับการสะท้อนของคลาสและยังมีความแตกต่างเช่นการพิมพ์และการดำเนินการ อะไรคือวิธีการนามธรรมของคลาสนามธรรมที่มีคำอธิบายประกอบโดยใช้วัตถุองค์ประกอบเพื่อวิเคราะห์คลาสนามธรรมที่มีคำอธิบายประกอบสร้างคลาสการใช้งาน (ไม่ใช่ abstract) ที่สืบทอดคลาสและใช้วิธีนามธรรมทั้งหมดในชั้นเรียน เนื่องจากวิธีการนามธรรมเหล่านี้จะไม่ถูกใช้จริงพวกเขาจึงต้องสามารถรวบรวมและผ่านได้ วิธีที่ฉันเลือกคือแต่ละวิธีการโยนข้อยกเว้นทำให้วิธีการนั้นเป็นวิธีที่เป็นนามธรรมและไม่สามารถเรียกได้โดยตรง วิธีการสร้างรหัสสามารถใช้เครื่องมือบางอย่างเพื่อทำให้การทำงานง่ายขึ้นเช่นตัวประมวลผลอัตโนมัติและ Javapoet ซึ่งใช้รหัสโครงการโดยเฉพาะเมื่อสิ้นสุดข้อความอ้างอิง รหัสที่สร้างขึ้นเป็นแบบนี้:
// ชื่อคลาสที่สร้างขึ้นนั้นมีชื่อพร้อมคำต่อท้ายของชื่อคลาสดั้งเดิม + "$ Impl" เพื่อหลีกเลี่ยงความขัดแย้งกับชื่อคลาสอื่น ๆ ข้อ จำกัด นี้ยังใช้เพื่อสะท้อนคลาสสาธารณะชั้นสุดท้าย Mockapi $ impl Expl Explates Mockapi {@Override สาธารณะที่สังเกตได้ <string> api1 () {โยนใหม่ lelveralStateException ("api1 () เป็นวิธีที่เป็นนามธรรม!"); } @Override สาธารณะที่สังเกตได้ <string> api2 () {โยน unlueLstateException ใหม่ ("api2 () เป็นวิธีนามธรรม!"); -สะท้อนให้เห็นถึงคลาสการใช้งานตามชื่อคลาสของคลาสนามธรรมจากนั้นสร้างวัตถุการใช้งานตามการสะท้อนโดยเรียกวิธีการสร้าง
// รับวัตถุที่สร้างรหัสสร้างส่วนตัวคงที่ <t> t getImplObject (คลาส <t> cls) {ลอง {return (t) class.forName (cls.getName () + "$ Impl") newInstance (); } catch (exception e) {return null; -สร้างพร็อกซีแบบไดนามิกส่งผ่านไปยังวัตถุจริงของ Realapi และวัตถุการใช้งานของคลาสนามธรรมที่สร้างขึ้นในขั้นตอนก่อนหน้าและกำหนดว่าวัตถุใดที่เป็นพร็อกซ์พฤติกรรมวิธีการตามคำจำกัดความในคลาสนามธรรม: หากมีคำจำกัดความในชั้นเรียนนามธรรมนั่นคือวิธีการที่เป็นนามธรรม มิฉะนั้นจะถูกดำเนินการโดยวัตถุจริงของอินเทอร์เฟซ
สาธารณะคงที่ <Origin, Mock ขยายต้นกำเนิด> Build Origin (แหล่งกำเนิดขั้นสุดท้าย, คลาสสุดท้าย <mock> mockclass) {// ถ้าคลาสเยาะเย้ยถูกทำเครื่องหมายว่าปิดมันจะส่งคืนวัตถุอินเตอร์เฟสจริงโดยตรงถ้า (! } mockObject mockObject สุดท้าย = getImplObject (mockclass); คลาส <?> OriginClass = Origin.getClass (). getInterfaces () [0]; return (Origin) Proxy.newProxyInstance (Originclass.getClassLoader (), คลาสใหม่ [] {Originclass}, ใหม่ InvocationHandler () {@Override วัตถุสาธารณะเรียกใช้ (Object O, วิธีการ, วัตถุ [] วัตถุ) mockclass.getDeclaredMethod (method.getName (), method.getParameterTypes ()); mockmethod.invoke (mockobject, วัตถุ);}}});}});}หลังจากเสร็จสิ้นการทำงานข้างต้นคุณสามารถใช้วิธีการสร้างเพื่อสร้างวัตถุพร็อกซีที่ผสมอินเทอร์เฟซจริงและวิธีการคลาสนามธรรมตามที่กล่าวไว้ในตอนต้น แม้ว่าคลาสการโทรจะเป็นรหัสที่ยาก แต่ก็ถูกสร้างขึ้นโดยอัตโนมัติโดยหน่วยประมวลผลคำอธิบายประกอบโดยไม่มีการบำรุงรักษาด้วยตนเอง ในแง่ของการใช้งานมันมักจะเหมือนกับการใช้งาน Javassist
ฉันใช้วิธีการที่ฉันอยู่ในบทความนี้เพื่อใช้เครื่องมือที่จำลองคำขอติดตั้งเพิ่มเติม (มีลิงก์ในตอนท้ายของบทความ) แต่โดยพื้นฐานแล้วมันสามารถนำมาใช้เพื่อใช้ความต้องการมากมายที่ต้องมีการสร้างคลาสนามธรรมและสถานการณ์การใช้งานเพิ่มเติมยังต้องสำรวจ
การใช้งานซอร์สโค้ดที่กล่าวถึงในบทความสามารถพบได้ในโครงการเพิ่มเติมเกี่ยวกับการติดตั้งเพิ่มเติมหรือดาวน์โหลดในท้องถิ่น
สรุป
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะมีค่าอ้างอิงบางอย่างสำหรับการศึกษาหรือที่ทำงานของทุกคน หากคุณมีคำถามใด ๆ คุณสามารถฝากข้อความไว้เพื่อสื่อสาร ขอบคุณสำหรับการสนับสนุน Wulin.com