Apa itu Metode Menangani di Jawa

1. Pendahuluan


Dalam tutorial ini kita akan melihat API penting yang diperkenalkan di Java 7 dan diperluas dalam versi baru, java.lang.invoke.MethodHandles .



Kita akan belajar apa pegangan metode, cara membuat dan menggunakannya.


2. Apa itu Metode Penanganan?


Dalam dokumentasi API, pegangan metode memiliki definisi ini:


Pegangan metode adalah referensi yang diketik, dapat dieksekusi ke metode dasar, konstruktor, bidang, atau operasi tingkat rendah lainnya dengan transformasi tambahan argumen atau nilai pengembalian.

Dengan kata lain, pegangan metode adalah mekanisme tingkat rendah untuk menemukan, mengadaptasi, dan memanggil metode. Pegangan metode tidak berubah dan tidak memiliki kondisi tampilan.


Untuk membuat dan menggunakan MethodHandle Anda perlu melakukan 4 langkah:


  1. Buat deskriptor pencarian - pencarian
  2. Nyatakan Jenis Metode
  3. Metode pencarian menangani
  4. Menangani metode panggilan

2.1. Metode Menangani vs Refleksi


Pegangan metode diperkenalkan berfungsi bersama dengan java.lang.reflect API , sebagai Mereka diciptakan untuk tujuan yang berbeda dan berbeda dalam karakteristik mereka.


Dalam hal kinerja, API MethodHandles bisa jauh lebih cepat daripada API Refleksi, karena pemeriksaan akses dilakukan pada waktu pembuatan daripada eksekusi . Jika ada manajer keamanan, perbedaan ini meningkat, karena mencari kelas dan mendapatkan elemen mereka akan dikenakan pemeriksaan tambahan.


Namun, kinerja bukan satu-satunya indikator optimalitas tugas, harus diingat bahwa MethodHandles API lebih sulit digunakan karena kurangnya mekanisme seperti mendapatkan metode kelas, memeriksa token akses, dll.


Meskipun demikian, API MethodHandles memungkinkan metode kari, mengubah jenis dan urutan parameter.


Sekarang, dengan mengetahui definisi dan tujuan API MethodHandles, kita dapat bekerja dengannya. Mari kita mulai dengan mencari metode.


3. Membuat Pencarian


Hal pertama yang harus dilakukan ketika kita ingin membuat pegangan metode adalah untuk mendapatkan pencarian, objek pabrik yang bertanggung jawab untuk membuat pegangan metode untuk metode, konstruktor, dan bidang yang terlihat oleh kelas pencarian.


Menggunakan API MethodHandles, Anda dapat membuat objek pencarian dengan berbagai mode akses.


Buat pencarian yang menyediakan akses ke metode publik:


 MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); 

Namun, jika kita memerlukan akses ke metode pribadi dan terlindungi, kita dapat menggunakan metode pencarian () sebagai gantinya:


 MethodHandles.Lookup lookup = MethodHandles.lookup(); 

4. Membuat MethodType


Untuk membuat MethodHandle, objek pencarian harus diatur ke tipe, dan ini bisa dilakukan menggunakan kelas MethodType.


Secara khusus, MethodType mewakili argumen dan tipe pengembalian yang diterima dan dikembalikan oleh pegangan metode, atau diteruskan dan diharapkan oleh kode panggilan.


Struktur MethodType sederhana, dibentuk oleh tipe pengembalian, bersama dengan jumlah tipe parameter yang sesuai, yang harus sepenuhnya berkorelasi antara pegangan metode dan kode panggilan.


Seperti MethodHandle, semua instance dari MethodType tidak dapat diubah.


Mari kita lihat bagaimana mendefinisikan MethodType yang mendefinisikan kelas java.util.List sebagai tipe return dan array Object sebagai tipe input data:


 MethodType mt = MethodType.methodType(List.class, Object[].class); 

Jika metode mengembalikan tipe nilai sederhana atau batal, kami menggunakan kelas yang mewakili tipe ini (void.class, int.class …) .


Tentukan MethodType yang mengembalikan int dan menerima Obyek:


 MethodType mt = MethodType.methodType(int.class, Object.class); 

Anda bisa mulai membuat MethodHandle.


5. Cari MethodHandle


Setelah kami menetapkan jenis metode, untuk membuat MethodHandle Anda harus menemukannya menggunakan objek pencarian atau publicLookup, yang juga mengembalikan kelas sumber dan nama metode.


Pencarian menyediakan serangkaian metode yang memungkinkan Anda menemukan pegangan metode secara optimal, dengan mempertimbangkan cakupan metode tersebut. Pertimbangkan pendekatan dasar, dimulai dengan yang paling sederhana.


5.1. Metode Menangani untuk Metode


Menggunakan metode findVirtual() , Anda bisa membuat MethodHandle untuk metode instance. Buat berdasarkan metode concat() dari kelas String :


 MethodType mt = MethodType.methodType(String.class, String.class); MethodHandle concatMH = publicLookup.findVirtual(String.class, "concat", mt); 

5.2. Metode Menangani untuk Metode Statis


Untuk mengakses metode statis, Anda dapat menggunakan metode findStatic() :


 MethodType mt = MethodType.methodType(List.class, Object[].class); MethodHandle asListMH = publicLookup.findStatic(Arrays.class, "asList", mt); 

Dalam hal ini, kami membuat pegangan metode untuk metode yang mengubah array dari objek tipe ke List .


5.3. Metode Menangani untuk Konstruktor


Anda dapat mengakses konstruktor menggunakan metode findConstructor() .


Buat pegangan metode dengan perilaku yang mirip dengan konstruktor kelas Integer dengan parameter String:


 MethodType mt = MethodType.methodType(void.class, String.class); MethodHandle newIntegerMH = publicLookup.findConstructor(Integer.class, mt); 

5.4. Metode Menangani untuk Bidang


Menggunakan pegangan metode, Anda juga dapat mengakses bidang.


Mari kita mulai dengan mendefinisikan kelas Buku:


 public class Book { String id; String title; // constructor } 

Sebagai kondisi awal, kami memiliki visibilitas langsung antara pegangan metode dan properti yang dideklarasikan, sehingga kami dapat membuat pegangan metode dengan perilaku yang mirip dengan metode get:


 MethodHandle getTitleMH = lookup.findGetter(Book.class, "title", String.class); 

Untuk informasi lebih lanjut tentang manajemen variabel / bidang, lihat artikel Java 9 Variable Handles Demystified , di mana kita berbicara tentang java.lang.invoke.VarHandle API yang diperkenalkan di Java 9.


5.5. Metode Menangani untuk Metode Pribadi


Anda bisa membuat pegangan metode untuk metode tipe privat menggunakan java.lang.reflect API .
Mari kita mulai dengan membuat metode pribadi untuk kelas Buku:


 private String formatBook() { return id + " > " + title; } 

Sekarang kita bisa membuat pegangan metode dengan perilaku metode formatBook() :


 Method formatBookMethod = Book.class.getDeclaredMethod("formatBook"); formatBookMethod.setAccessible(true); MethodHandle formatBookMH = lookup.unreflect(formatBookMethod); 

6. Menangani Metode Panggilan


Setelah kami membuat pegangan metode kami, lanjutkan ke langkah berikutnya. Kelas MethodHandle memberi kita 3 cara berbeda untuk memanggil metode handle: invokeWithArugments() , invokeWithArugments() dan invokeExact() .


Mari kita mulai dengan metode invoke .


6.1. Menangani Metode Panggilan


Saat menggunakan metode invoke() , jumlah argumen (arity) sudah diperbaiki, tetapi dimungkinkan untuk melakukan konversi jenis dan mengepak / membongkar argumen dan jenis nilai balik.


Sekarang mari kita lihat bagaimana invoke() dapat digunakan dengan argumen penuh:


 MethodType mt = MethodType.methodType(String.class, char.class, char.class); MethodHandle replaceMH = publicLookup.findVirtual(String.class, "replace", mt); String output = (String) replaceMH.invoke("jovo", Character.valueOf('o'), 'a'); assertEquals("java", output); 

Dalam kasus ini, replaceMH membutuhkan argumen char , tetapi metode replaceMH invoke() mendekompresi argumen Character sebelum dieksekusi.


6.2. Panggil dengan argumen


Menangani metode invokeWithArguments menggunakan invokeWithArguments memiliki batasan paling sedikit.


Bahkan, selain memeriksa jenis dan mengemas / membongkar argumen dan mengembalikan nilai, ini memungkinkan Anda untuk melakukan panggilan dengan sejumlah variabel parameter.


Dalam praktiknya, kita dapat membuat daftar Integer dengan array nilai int yang panjangnya tidak diketahui:


 MethodType mt = MethodType.methodType(List.class, Object[].class); MethodHandle asList = publicLookup.findStatic(Arrays.class, "asList", mt); List<Integer> list = (List<Integer>) asList.invokeWithArguments(1, 2); assertThat(Arrays.asList(1,2), is(list)); 

6.3. Panggil tepat


Jika kita membutuhkan pegangan metode untuk dieksekusi lebih ketat (dengan set argumen dan jenisnya), kita menggunakan metode invokeExact() .


Bahkan, itu tidak menyediakan kemampuan untuk melemparkan tipe kelas dan membutuhkan seperangkat argumen tetap.


Mari kita lihat bagaimana kita dapat menambahkan dua nilai int menggunakan metode handle:


 MethodType mt = MethodType.methodType(int.class, int.class, int.class); MethodHandle sumMH = lookup.findStatic(Integer.class, "sum", mt); int sum = (int) sumMH.invokeExact(1, 11); assertEquals(12, sum); 

Dalam hal ini, jika kami memberikan nomor yang bukan int ke metode invokeExact , saat kami menelepon, kami mendapatkan WrongMethodTypeException .


7. Bekerja dengan array


MethodHandles dapat bekerja tidak hanya dengan bidang dan objek, tetapi juga dengan array. Menggunakan API asSpreader() , Anda bisa membuat pegangan metode yang mendukung array sebagai argumen posisional.


Dalam hal ini, pegangan metode mengambil array, mendistribusikan elemen-elemennya sebagai argumen posisi, dan secara opsional, panjang array.


Mari kita lihat bagaimana cara menangani metode untuk memeriksa apakah argumen array adalah string yang sama:


 MethodType mt = MethodType.methodType(boolean.class, Object.class); MethodHandle equals = publicLookup.findVirtual(String.class, "equals", mt); MethodHandle methodHandle = equals.asSpreader(Object[].class, 2); assertTrue((boolean) methodHandle.invoke(new Object[] { "java", "java" })); 

8. Klarifikasi Metode Menangani


Setelah pegangan metode diatur, Anda dapat memperbaikinya dengan mengikat argumen, tanpa memanggil metode.


Misalnya, di Java 9, trik ini digunakan untuk mengoptimalkan penggabungan string.


Mari kita lihat bagaimana penggabungan dapat dilakukan dengan menempelkan akhiran ke concatMH :


 MethodType mt = MethodType.methodType(String.class, String.class); MethodHandle concatMH = publicLookup.findVirtual(String.class, "concat", mt); MethodHandle bindedConcatMH = concatMH.bindTo("Hello "); assertEquals("Hello World!", bindedConcatMH.invoke("World!")); 

9. Pembaruan Java 9


Java 9 memperkenalkan beberapa perubahan pada MethodHandles API untuk membuatnya lebih mudah digunakan.


Pembaruan menyangkut 3 aspek utama:


  • Fungsi pencarian - memungkinkan pencarian dari konteks yang berbeda dan mendukung metode non-abstrak dalam antarmuka.
  • Operasi dengan argumen - meningkatkan fungsi melipat, mengumpulkan, dan mendistribusikan argumen.
  • Kombinasi tambahan menambahkan operasi loop ( loop , whileLoop , doWhileLoop , ...) dan manajemen pengecualian ditingkatkan dengan tryFinally .

Perubahan ini mensyaratkan inovasi berguna lainnya:


  • Optimasi JVM Compiler yang ditingkatkan
  • Instance menurun
  • Konkretisasi penggunaan API MethodHandles

Daftar perubahan yang lebih terperinci tersedia di Javadoc MethodHandles API .


10. Kesimpulan


Dalam artikel ini, kami bertemu dengan MethodHandles API, dan juga mempelajari apa itu Metode Penanganan dan bagaimana cara menggunakannya.


Kami juga menjelaskan bagaimana ini terkait dengan API Refleksi. Karena pegangan metode panggilan adalah operasi tingkat yang agak rendah, penggunaannya hanya dibenarkan jika benar-benar sesuai dengan tugas Anda.


Seperti biasa, semua kode sumber untuk artikel tersedia di Github .

Source: https://habr.com/ru/post/id431922/


All Articles