Jawaban:
Ini adalah instruksi JVM baru yang memungkinkan kompiler untuk menghasilkan kode yang memanggil metode dengan spesifikasi yang lebih longgar daripada sebelumnya - jika Anda tahu apa itu " mengetik bebek ", invokedynamic pada dasarnya memungkinkan untuk mengetik bebek. Tidak terlalu banyak yang bisa Anda lakukan sebagai programmer Java; jika Anda seorang pembuat alat, Anda dapat menggunakannya untuk membangun bahasa berbasis JVM yang lebih fleksibel dan lebih efisien. Berikut ini adalah posting blog yang sangat manis yang memberikan banyak detail.
MethodHandle
, yang benar-benar hal yang sama tetapi dengan lebih banyak fleksibilitas. Tetapi kekuatan sebenarnya dalam semua ini bukan berasal dari bahasa Jawa, tetapi pada kemampuan JVM sendiri dalam mendukung bahasa lain yang secara intrinsik lebih dinamis.
invokedynamic
yang menjadikannya performant (dibandingkan dengan membungkusnya dalam kelas-anonim yang hampir menjadi satu-satunya pilihan sebelum memperkenalkan invokedynamic
). Kemungkinan besar banyak bahasa pemrograman fungsional di atas JVM akan memilih untuk mengkompilasi ini daripada kelas-dalam-dalam.
Beberapa waktu lalu, C # menambahkan fitur keren, sintaks dinamis dalam C #
Object obj = ...; // no static type available
dynamic duck = obj;
duck.quack(); // or any method. no compiler checking.
Anggap saja sebagai sintaks gula untuk panggilan metode reflektif. Ini dapat memiliki aplikasi yang sangat menarik. lihat http://www.infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter
Neal Gafter, yang bertanggung jawab untuk tipe dinamis C #, baru saja membelot dari SUN ke MS. Jadi tidak masuk akal untuk berpikir bahwa hal yang sama telah dibahas di dalam SUN.
Saya ingat segera setelah itu, beberapa pria Jawa mengumumkan sesuatu yang serupa
InvokeDynamic duck = obj;
duck.quack();
Sayangnya, fitur ini tidak dapat ditemukan di Java 7. Sangat kecewa. Untuk programmer Java, mereka tidak memiliki cara mudah untuk memanfaatkan invokedynamic
program mereka.
invokedynamic
tidak pernah dimaksudkan untuk digunakan untuk programmer Java. IMO itu tidak sesuai dengan filosofi Java sama sekali. Itu ditambahkan sebagai fitur JVM untuk bahasa non-Jawa.
Ada dua konsep untuk dipahami sebelum melanjutkan ke invokedynamic.
1. Pengetikan Statis vs. Dynamin
Static - pengecekan tipe preforms pada waktu kompilasi (misal Java)
Dynamic - pemeriksaan tipe preforms saat runtime (mis. JavaScript)
Pengecekan tipe adalah proses memverifikasi bahwa suatu program adalah tipe aman, ini adalah, memeriksa informasi yang diketik untuk variabel kelas dan instance, parameter metode, nilai kembali, dan variabel lainnya. Misalnya Java tahu tentang int, String, .. pada waktu kompilasi, sedangkan jenis objek dalam JavaScript hanya dapat ditentukan saat runtime
2. Mengetik Kuat vs Lemah
Strong - menetapkan batasan pada jenis nilai yang disediakan untuk operasinya (mis. Java)
Lemah - mengkonversi (melemparkan) argumen operasi jika argumen tersebut memiliki tipe yang tidak kompatibel (mis. Visual Basic)
Mengetahui bahwa Java adalah jenis yang Statically and Weakly typed, bagaimana Anda mengimplementasikan bahasa yang diketik secara Dinamis dan Kuat pada JVM?
Invokedynamic mengimplementasikan sistem runtime yang dapat memilih implementasi metode atau fungsi yang paling tepat - setelah program dikompilasi.
Contoh: Memiliki (a + b) dan tidak tahu apa-apa tentang variabel a, b pada waktu kompilasi, invokedynamic memetakan operasi ini ke metode yang paling tepat di Java saat runtime. Misalnya, jika ternyata a, b adalah Strings, maka panggil metode (String a, String b). Jika ternyata a, b adalah ints, maka panggil metode (int a, int b).
invokedynamic diperkenalkan dengan Java 7.
Sebagai bagian dari artikel Java Records saya, saya mengartikulasikan tentang motivasi di balik Inoke Dynamic. Mari kita mulai dengan definisi kasar dari Indy.
Invoke Dynamic (Juga dikenal sebagai Indy ) adalah bagian dari JSR 292 yang bermaksud untuk meningkatkan dukungan JVM untuk Dynamic Type Languages. Setelah rilis pertama di Java 7, invokedynamic
opcode beserta java.lang.invoke
kopernya digunakan cukup luas oleh bahasa berbasis JVM dinamis seperti JRuby.
Meskipun indy dirancang khusus untuk meningkatkan dukungan bahasa dinamis, indy menawarkan lebih dari itu. Sebenarnya, ini cocok untuk digunakan di mana pun perancang bahasa membutuhkan segala bentuk dinamika, dari akrobat tipe dinamis hingga strategi dinamis!
Misalnya, Java 8 Lambda Expressions sebenarnya diimplementasikan menggunakan invokedynamic
, meskipun Java adalah bahasa yang diketik secara statis!
Untuk beberapa waktu JVM mendukung empat tipe pemanggilan metode: invokestatic
memanggil metode statis, invokeinterface
memanggil metode antarmuka, invokespecial
memanggil konstruktor, super()
atau metode pribadi dan invokevirtual
memanggil metode instan.
Terlepas dari perbedaan mereka, tipe doa ini memiliki satu sifat yang sama: kita tidak dapat memperkaya mereka dengan logika kita sendiri . Sebaliknya, invokedynamic
memungkinkan kita untuk Bootstrap proses doa dengan cara apa pun yang kita inginkan. Kemudian JVM menangani memanggil Metode Bootstrapped secara langsung.
Pertama kali JVM melihat invokedynamic
instruksi, ia memanggil metode statis khusus yang disebut Metode Bootstrap . Metode bootstrap adalah bagian dari kode Java yang telah kami tulis untuk menyiapkan logika yang akan dipanggil:
Kemudian metode bootstrap mengembalikan instance dari java.lang.invoke.CallSite
. Ini CallSite
memegang referensi ke metode aktual, yaitu MethodHandle
.
Mulai sekarang, setiap kali JVM melihat invokedynamic
instruksi ini lagi, ia melompati Slow Path dan secara langsung memanggil executable yang mendasarinya. JVM terus melewati jalur lambat kecuali ada perubahan.
Java 14 Records
menyediakan sintaksis kompak yang bagus untuk mendeklarasikan kelas yang seharusnya menjadi pemegang data bodoh.
Mempertimbangkan catatan sederhana ini:
public record Range(int min, int max) {}
Bytecode untuk contoh ini akan menjadi sesuatu seperti:
Compiled from "Range.java"
public java.lang.String toString();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokedynamic #18, 0 // InvokeDynamic #0:toString:(LRange;)Ljava/lang/String;
6: areturn
Dalam Tabel Metode Bootstrapnya :
BootstrapMethods:
0: #41 REF_invokeStatic java/lang/runtime/ObjectMethods.bootstrap:
(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;
Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;
Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
Method arguments:
#8 Range
#48 min;max
#50 REF_getField Range.min:I
#51 REF_getField Range.max:I
Jadi metode bootstrap untuk Records disebut bootstrap
yang berada di java.lang.runtime.ObjectMethods
kelas. Seperti yang Anda lihat, metode bootstrap ini mengharapkan parameter berikut:
MethodHandles.Lookup
mewakili konteks pencarian (Bagian Ljava/lang/invoke/MethodHandles$Lookup
).toString
, equals
, hashCode
, dll) bootstrap yang akan untuk nge-link. Misalnya, ketika nilainya adalah toString
, bootstrap akan mengembalikan a ConstantCallSite
( CallSite
yang tidak pernah berubah) yang menunjuk ke toString
implementasi aktual untuk Catatan khusus ini.TypeDescriptor
metode ( Ljava/lang/invoke/TypeDescriptor
bagian).Class<?>
, mewakili tipe kelas Rekam. Dalam
Class<Range>
kasus ini.min;max
.MethodHandle
per komponen. Dengan cara ini metode bootstrap dapat membuat MethodHandle
berbasis pada komponen untuk implementasi metode khusus ini.The invokedynamic
instruksi melewati semua argumen mereka dengan metode bootstrap. Metode bootstrap, pada gilirannya, mengembalikan sebuah instance dari ConstantCallSite
. Ini ConstantCallSite
memegang referensi untuk implementasi metode yang diminta, misalnya toString
.
Berbeda dengan Reflection APIs, java.lang.invoke
API ini cukup efisien karena JVM dapat sepenuhnya melihat semua permintaan. Karena itu, JVM dapat menerapkan segala macam optimasi selama kita menghindari jalan lambat sebanyak mungkin!
Selain argumen efisiensi, invokedynamic
pendekatan ini lebih dapat diandalkan dan kurang rapuh karena kesederhanaannya .
Selain itu, bytecode yang dihasilkan untuk Java Records tidak tergantung pada jumlah properti. Jadi, bytecode lebih sedikit dan waktu startup lebih cepat.
Akhirnya, anggaplah versi baru Java menyertakan implementasi metode bootstrap baru dan lebih efisien. Dengan invokedynamic
, aplikasi kami dapat memanfaatkan peningkatan ini tanpa kompilasi ulang. Dengan cara ini kita memiliki semacam Kompatibilitas Biner Teruskan . Juga, Itulah strategi dinamis yang sedang kita bicarakan!
Selain Java Records, dinamis yang dipanggil telah digunakan untuk mengimplementasikan fitur-fitur seperti:
LambdaMetafactory
StringConcatFactory
meth.invoke(args)
. Jadi bagaimanainvokedynamic
cocoknyameth.invoke
?