Kata pengantar
Baru -baru ini, selama proses pembelajaran, saya menemukan masalah. Kelas abstrak tidak dapat membangun objek melalui baru tanpa menerapkan semua metode abstrak, tetapi metode abstrak dapat memiliki metode konstruksi sendiri. Ini membuat saya bingung. Karena ada metode konstruksi dan tidak dapat dibuat melalui kelas -kelas abstrak yang baru, dapatkah dipakai ketika mereka belum menjadi kelas konkret?
Di Java, kelas abstrak tidak dapat dipakai secara langsung. Namun, fitur kelas abstrak ini sering menjadi hambatan yang merepotkan. Misalnya, jika saya ingin menggunakan proxy dinamis untuk memberikan kelas abstrak kemampuan untuk menjalankan metode abstrak, akan ada dua kesulitan: 1. Proxy dinamis hanya dapat membuat objek proxy yang mengimplementasikan antarmuka, tetapi bukan objek yang mewarisi kelas abstrak. Untuk JVM standar ini ada beberapa implementasi, seperti Javassist, yang dapat digunakan untuk mencapai ini menggunakan alat bytecode (proxyFactory).
Jika Anda ingin membuat objek kelas abstrak di Android, Anda mungkin hanya memiliki new ClassName() {} atau diwariskan. Namun, kedua metode tidak dapat dioperasikan secara langsung oleh objek kelas mereka, yang mengarah pada beberapa masalah yang tidak dapat mencapai kemampuan abstrak yang kita butuhkan.
Berikut adalah deskripsi terperinci tentang adegan yang disebutkan dalam paragraf pertama:
Pertama, ada file antarmuka yang didefinisikan sebagai berikut (teman yang akrab dengan Android dapat melihat bahwa ini adalah antarmuka konfigurasi API yang disediakan untuk retrofit untuk menghasilkan objek proxy):
antarmuka publik realapi {@get ("API1") Observable <string> API1 (); @Get ("API2") Observable <string> API2 (); @Get ("API3") Observable <string> API3 (); //... Metode lain}Selanjutnya, tulis kelas abstrak untuk mengimplementasikan hanya salah satu metode antarmuka (digunakan untuk mensimulasikan data antarmuka):
@Mockapipubublic kelas abstrak mockapi mengimplementasikan realapi {Observable <string> API3 () {return Observable.just ("Mock Data"); }}Maka kita perlu memiliki alat, seperti MockManager, yang menggabungkan kelas Realapi Object dan Mockapi kami yang ada untuk membangun objek campuran. Saat mengeksekusi metode yang sudah didefinisikan dalam mockapi, itu akan secara langsung menjalankannya. Ketika mockapi tidak mendefinisikan metode ini, itu akan memanggil metode realapi. Metode panggilan kira -kira:
Realapi API = mockManager.build (realapi, mockapi.class);
Melalui Javassist, sangat mudah untuk menyelesaikan fungsi di atas. Buat objek proxyFactory, atur superclassnya ke mockapi, lalu filter metode abstrak, dan atur pawang metode untuk memanggil objek realapi dengan nama dan metode parameter yang sama. Implementasi kode tidak akan diberikan di sini.
Tapi di Android, metode Javassist akan dilemparkan
Disebabkan oleh: java.lang.unsupportedOperationException: Tidak dapat memuat jenis file kelas ini di java.lang.classloader.defineClass (classloader.java:520) di java.lang.reflect.method.invoke (metode asli) di javassist.util.proxy.factoryhelper.toclass2 (factoryhelper.java:182)
Pengecualian serupa. Alasannya mungkin karena implementasi dan standar mesin virtual di Android sedikit berbeda, jadi di sini kita mengubah arah ke arah lain dari prosesor anotasi pembuatan kode dinamis.
Jika Anda menggunakan prosesor anotasi untuk mengimplementasikannya, idenya jauh lebih sederhana, tetapi prosesnya masih sedikit berliku:
Pertama -tama tentukan anotasi untuk menandai kelas abstrak yang perlu dibangun
@Target (elementType.type)@didokumentasikan@retensi (retentionpolicy.source) public @interface mockapi {} Prosesor memperoleh objek elemen kelas berdasarkan anotasi, yang merupakan objek seperti kelas. Karena kelas belum ada pada tahap prekompilasi, tidak mungkin untuk mendapatkan objek kelas yang dibutuhkan oleh runtime menggunakan Class.forName . Namun, elemen menyediakan metode yang mirip dengan kelas refleksi, dan ada juga perbedaan seperti typeelement dan executableElement. Apa metode abstrak dari kelas abstrak yang dianotasi menggunakan objek elemen untuk menganalisis kelas abstrak beranotasi, menghasilkan kelas implementasi (non-abstrak) yang mewarisi kelas, dan mengimplementasikan semua metode abstrak di kelas. Karena metode abstrak ini tidak akan benar -benar digunakan, mereka hanya perlu dapat dikompilasi dan dilewati. Cara yang saya pilih adalah bahwa masing -masing badan metode melempar pengecualian, mendorong bahwa metode ini adalah metode abstrak dan tidak dapat dipanggil secara langsung. Metode menghasilkan kode dapat menggunakan beberapa alat untuk menyederhanakan pekerjaan, seperti autoprosesor dan javapoet, yang secara khusus mengimplementasikan kode proyek pada akhir teks referensi. Kode yang dihasilkan kira -kira seperti ini:
// Nama kelas yang dihasilkan dinamai dengan akhiran dari nama kelas asli + "$ IMP" untuk menghindari konflik dengan nama kelas lainnya. Kendala ini juga digunakan untuk mencerminkan kelas akhir kelas publik mockapi $ IMP memperluas mockapi {@override publik yang dapat diamati <string> API1 () {lempar baru ilegalstateException ("API1 () adalah metode abstrak!"); } @Override public Observable <String> API2 () {Throw new IllegalStateException ("API2 () adalah metode abstrak!"); }}Mencerminkan kelas implementasi berdasarkan nama kelas kelas abstrak, dan kemudian membangun objek implementasi berdasarkan refleksi dengan memanggil metode konstruktornya.
// Dapatkan objek yang menghasilkan kode membangun private static <t> t getImplObject (class <t> cls) {coba {return (t) class.forname (cls.getname () + "$ impl"). NewInstance (); } catch (Exception e) {return null; }}Bangun proxy dinamis, masukkan ke dalam objek nyata Realapi, dan objek implementasi dari kelas abstrak yang dibangun pada langkah sebelumnya, dan tentukan objek mana yang membuat metodenya mendasarkan pada definisi dalam kelas abstrak: jika ada definisi dalam kelas abstrak, yaitu metode ini bukan metode abstrak, objek implementasi dari kelas abstrak akan dieksekusi; Kalau tidak, itu akan dieksekusi oleh objek nyata antarmuka.
Public Static <Origin, Mock Extends Origin> Origin Build (Asal Asal Akhir, Kelas Akhir <mock> mockClass) {// Jika kelas tiruan ditandai sebagai ditutup, itu akan secara langsung mengembalikan objek antarmuka nyata jika (! isEnable (mockclass)) {return asal; } final mock mockObject = getImplObject (mockclass); Class <?> Originclass = origin.getClass (). GetInterfaces () [0]; return (Origin) Proxy.newProxyInstance(originClass.getClassLoader(), new Class[]{originClass}, new InvocationHandler() { @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { // Get the method of the same name in the defined abstract class and determine whether the Method mockMethod = null; try { mockMethod = mockclass.getDeclaredMethod (method.getName (), method.getParametertypes ()); mockmethod.invoke (mockobject, objek);}});}});}Setelah menyelesaikan pekerjaan di atas, Anda dapat menggunakan metode build untuk membangun objek proxy yang mencampur antarmuka nyata dan metode kelas abstrak seperti yang disebutkan di awal. Meskipun kelas panggilan pada dasarnya dikodekan keras, secara otomatis dihasilkan oleh prosesor anotasi tanpa pemeliharaan manual. Dalam hal penggunaan, pada dasarnya sama dengan menggunakan implementasi javassist.
Saya menggunakan metode yang saya miliki dalam artikel ini untuk mengimplementasikan alat yang mensimulasikan permintaan retrofit (ada tautan di akhir artikel), tetapi pada dasarnya, dapat digunakan untuk menerapkan banyak kebutuhan yang memerlukan pembangunan kelas abstrak, dan lebih banyak skenario penggunaan masih perlu dieksplorasi.
Implementasi kode sumber yang disebutkan dalam artikel dapat ditemukan dalam proyek retrofit-mock-result atau unduhan lokal;
Meringkaskan
Di atas adalah seluruh konten artikel ini. Saya berharap konten artikel ini memiliki nilai referensi tertentu untuk studi atau pekerjaan semua orang. Jika Anda memiliki pertanyaan, Anda dapat meninggalkan pesan untuk berkomunikasi. Terima kasih atas dukungan Anda ke wulin.com.