Câu trả lời:
Đó là một lệnh JVM mới cho phép trình biên dịch tạo mã gọi các phương thức với đặc tả lỏng hơn so với trước đây - nếu bạn biết " gõ vịt " là gì, về cơ bản inv invocate cho phép gõ vịt. Không có quá nhiều bạn là một lập trình viên Java có thể làm với nó; Tuy nhiên, nếu bạn là người tạo công cụ, bạn có thể sử dụng nó để xây dựng các ngôn ngữ dựa trên JVM linh hoạt hơn, hiệu quả hơn. Dưới đây là một bài viết blog thực sự ngọt ngào cung cấp rất nhiều chi tiết.
MethodHandle
, đây thực sự là một loại điều tương tự nhưng với sự linh hoạt hơn nhiều. Nhưng sức mạnh thực sự trong tất cả những điều này không chỉ bổ sung cho ngôn ngữ Java, mà ở khả năng của chính JVM trong việc hỗ trợ các ngôn ngữ khác thực chất năng động hơn.
invokedynamic
nó làm cho nó hoạt động (so với việc bọc chúng trong một lớp bên trong ẩn danh gần như là lựa chọn duy nhất trước khi giới thiệu invokedynamic
). Hầu hết có lẽ rất nhiều ngôn ngữ lập trình chức năng trên JVM sẽ chọn để biên dịch sang ngôn ngữ này thay vì các lớp bên trong.
Cách đây một thời gian, C # đã thêm một tính năng thú vị, cú pháp động trong C #
Object obj = ...; // no static type available
dynamic duck = obj;
duck.quack(); // or any method. no compiler checking.
Hãy nghĩ về nó như cú pháp đường cho các cuộc gọi phương thức phản chiếu. Nó có thể có các ứng dụng rất thú vị. xem http://www.infoq.com/presentations/Statically-Docate-Typing-Neal-Gafter
Neal Gafter, người chịu trách nhiệm cho loại động của C #, vừa chuyển từ SUN sang MS. Vì vậy, không có lý khi nghĩ rằng những điều tương tự đã được thảo luận trong SUN.
Tôi nhớ ngay sau đó, một số anh chàng Java đã thông báo một cái gì đó tương tự
InvokeDynamic duck = obj;
duck.quack();
Thật không may, tính năng này không có ở đâu trong Java 7. Rất thất vọng. Đối với các lập trình viên Java, họ không có cách nào dễ dàng để tận dụng lợi thế invokedynamic
trong các chương trình của họ.
invokedynamic
không bao giờ có ý định được sử dụng cho các lập trình viên Java. IMO nó hoàn toàn không phù hợp với triết lý Java. Nó đã được thêm vào như một tính năng JVM cho các ngôn ngữ không phải Java.
Có hai khái niệm cần hiểu trước khi tiếp tục phát sinh.
1. Nhập tĩnh so với nhập dữ liệu
Tĩnh - kiểm tra kiểu phôi ở thời gian biên dịch (ví dụ Java)
Dynamic - kiểm tra kiểu phôi trong thời gian chạy (ví dụ: JavaScript)
Kiểm tra kiểu là một quá trình xác minh rằng một chương trình là loại an toàn, đây là, kiểm tra thông tin đã nhập cho các biến lớp và thể hiện, tham số phương thức, giá trị trả về và các biến khác. Ví dụ: Java biết về int, String, .. tại thời gian biên dịch, trong khi loại đối tượng trong JavaScript chỉ có thể được xác định khi chạy
2. Đánh máy mạnh so với yếu
Mạnh - chỉ định các hạn chế đối với các loại giá trị được cung cấp cho các hoạt động của nó (ví dụ: Java)
Yếu - chuyển đổi (phôi) đối số của một hoạt động nếu các đối số đó có loại không tương thích (ví dụ: Visual Basic)
Biết rằng Java là một kiểu gõ tĩnh và yếu, làm thế nào để bạn triển khai các ngôn ngữ được gõ một cách linh hoạt và mạnh mẽ trên JVM?
Inv invocate thực hiện một hệ thống thời gian chạy có thể chọn triển khai phương thức hoặc hàm thích hợp nhất - sau khi chương trình được biên dịch.
Ví dụ: Có (a + b) và không biết gì về các biến a, b tại thời gian biên dịch, inv invocate ánh xạ thao tác này sang phương thức thích hợp nhất trong Java khi chạy. Ví dụ: nếu hóa ra a, b là Chuỗi, thì gọi phương thức (Chuỗi a, Chuỗi b). Nếu hóa ra a, b là ints, thì gọi phương thức (int a, int b).
invokeocate đã được giới thiệu với Java 7.
Là một phần của Hồ sơ Java của tôi bài viết , tôi đã nói rõ về động lực đằng sau Inoke Dynamic. Hãy bắt đầu với một định nghĩa sơ bộ về Indy.
Invoke Dynamic (Còn được gọi là Indy ) là một phần của JSR 292 có ý định tăng cường hỗ trợ JVM cho Ngôn ngữ loại động. Sau lần phát hành đầu tiên trong Java 7, invokedynamic
Opcode cùng với nójava.lang.invoke
hành lý được sử dụng khá nhiều bởi các ngôn ngữ dựa trên JVM động như JRuby.
Mặc dù indy được thiết kế đặc biệt để tăng cường hỗ trợ ngôn ngữ động, nhưng nó cung cấp nhiều hơn thế. Thực tế, nó phù hợp để sử dụng bất cứ nơi nào một nhà thiết kế ngôn ngữ cần bất kỳ hình thức năng động nào, từ nhào lộn kiểu động đến chiến lược năng động!
Chẳng hạn, các biểu thức Lambda của Java 8 thực sự được triển khai bằng cách sử dụng invokedynamic
, mặc dù Java là một ngôn ngữ được gõ tĩnh!
Trong một thời gian, JVM đã hỗ trợ bốn kiểu gọi phương thức: invokestatic
gọi phương thức tĩnh, invokeinterface
gọi phương thức giao diện, invokespecial
gọi hàm tạo super()
hoặc phương thức riêng tư vàinvokevirtual
gọi phương thức cá thể.
Bất chấp sự khác biệt của chúng, các kiểu gọi này có chung một đặc điểm: chúng ta không thể làm phong phú chúng bằng logic riêng của chúng ta . Ngược lại, invokedynamic
cho phép chúng tôi Bootstrap quá trình gọi theo bất kỳ cách nào chúng tôi muốn. Sau đó, JVM sẽ gọi trực tiếp Phương thức Bootstrapping.
Lần đầu tiên JVM nhìn thấy một invokedynamic
lệnh, nó gọi một phương thức tĩnh đặc biệt gọi là Phương thức Bootstrap . Phương thức bootstrap là một đoạn mã Java mà chúng ta đã viết để chuẩn bị logic thực tế được gọi:
Sau đó, phương thức bootstrap trả về một thể hiện của java.lang.invoke.CallSite
. Điều này CallSite
giữ một tham chiếu đến phương thức thực tế, tức làMethodHandle
.
Từ giờ trở đi, mỗi khi JVM nhìn thấy invokedynamic
hướng dẫn này một lần nữa, nó sẽ bỏ qua Đường dẫn chậm và gọi trực tiếp tệp thực thi cơ bản. JVM tiếp tục bỏ qua đường dẫn chậm trừ khi có gì đó thay đổi.
Java 14 Records
đang cung cấp một cú pháp nhỏ gọn đẹp mắt để khai báo các lớp được cho là chủ sở hữu dữ liệu câm.
Xem xét hồ sơ đơn giản này:
public record Range(int min, int max) {}
Mã byte cho ví dụ này sẽ là một cái gì đó như:
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
Trong Bảng Phương thức Bootstrap của nó :
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
Vì vậy, phương thức bootstrap cho Records được gọi là bootstrap
nằm trong java.lang.runtime.ObjectMethods
lớp. Như bạn có thể thấy, phương thức bootstrap này mong đợi các tham số sau:
MethodHandles.Lookup
đại diện cho bối cảnh tra cứu (Phần Ljava/lang/invoke/MethodHandles$Lookup
).toString
, equals
, hashCode
, vv) bootstrap sẽ liên kết. Ví dụ: khi giá trị là toString
, bootstrap sẽ trả về một ConstantCallSite
( CallSite
không bao giờ thay đổi) chỉ ra việc toString
triển khai thực tế cho Bản ghi cụ thể này.TypeDescriptor
cho phương pháp này (Ljava/lang/invoke/TypeDescriptor
một phần).Class<?>
, đại diện cho loại lớp Ghi. nó là
Class<Range>
trong trường hợp này.min;max
.MethodHandle
cho mỗi thành phần. Bằng cách này, phương thức bootstrap có thể tạo ra MethodHandle
dựa trên các thành phần để thực hiện phương thức cụ thể này.Lệnh này invokedynamic
chuyển tất cả các đối số đó sang phương thức bootstrap. Phương thức Bootstrap, trả về một thể hiện của ConstantCallSite
. Điều này ConstantCallSite
đang giữ một tài liệu tham khảo để thực hiện phương pháp được yêu cầu, ví dụ toString
.
Trái ngược với API phản chiếu, java.lang.invoke
API khá hiệu quả do JVM hoàn toàn có thể nhìn thấy qua tất cả các yêu cầu. Do đó, JVM có thể áp dụng tất cả các loại tối ưu hóa miễn là chúng ta tránh được đường dẫn chậm nhất có thể!
Ngoài các đối số hiệu quả, invokedynamic
cách tiếp cận đáng tin cậy hơn và ít giòn hơn vì tính đơn giản của nó .
Hơn nữa, mã byte được tạo cho Bản ghi Java độc lập với số lượng thuộc tính. Vì vậy, ít mã byte hơn và thời gian khởi động nhanh hơn.
Cuối cùng, giả sử một phiên bản Java mới bao gồm triển khai phương thức bootstrap mới và hiệu quả hơn. Với invokedynamic
, ứng dụng của chúng tôi có thể tận dụng sự cải tiến này mà không cần biên dịch lại. Bằng cách này, chúng tôi có một số loại tương thích nhị phân chuyển tiếp . Ngoài ra, đó là chiến lược năng động mà chúng ta đang nói đến!
Ngoài Java Records, động gọi ra đã được sử dụng để triển khai các tính năng như:
LambdaMetafactory
StringConcatFactory
meth.invoke(args)
. Vậy làm thế nào đểinvokedynamic
phù hợp vớimeth.invoke
?