Yanıtlar:
Bir derleyicinin daha önce mümkün olandan daha gevşek bir spesifikasyona sahip yöntemleri çağıran kod üretmesine izin veren yeni bir JVM talimatıdır - " ördek yazmanın " ne olduğunu biliyorsanız , invokedynamic temelde ördek yazmaya izin verir. Bir Java programcısı onunla yapabileceğiniz kadar çok şey yok; yine de bir araç oluşturucuysanız, daha esnek, daha verimli JVM tabanlı diller oluşturmak için kullanabilirsiniz. İşte çok fazla ayrıntı veren gerçekten tatlı bir blog yazısı.
MethodHandle
, gerçekten aynı şey ama çok daha fazla esnekliğe sahip. Ancak tüm bunlardaki gerçek güç, Java diline ek olarak değil, JVM'nin kendiliğinden daha dinamik olan diğer dilleri destekleme yeteneklerinde gelir.
invokedynamic
performans gösterdiği anlaşılıyor (onları tanıtmadan önce neredeyse tek seçenek olan anonim bir iç sınıfa sarmakla karşılaştırıldığında invokedynamic
). Muhtemelen JVM'nin üstünde bir çok fonksiyonel programlama dili, anon-iç sınıflar yerine bunu derlemeyi seçecektir.
Bir süre önce, C # serin bir özellik, C # içine dinamik sözdizimi ekledi
Object obj = ...; // no static type available
dynamic duck = obj;
duck.quack(); // or any method. no compiler checking.
Yansıtıcı yöntem çağrıları için sözdizimi şekeri olarak düşünün. Çok ilginç uygulamalara sahip olabilir. bkz. http://www.infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter
C # 'ın dinamik türünden sorumlu olan Neal Gafter, SUN'dan MS'ye kaçtı. Dolayısıyla aynı şeylerin SUN içinde tartışıldığını düşünmek mantıksız değil.
Bundan kısa süre sonra hatırlıyorum, bazı Java dude benzer bir şey açıkladı
InvokeDynamic duck = obj;
duck.quack();
Ne yazık ki, özellik Java 7 bulunacak bir yer değildir. Çok hayal kırıklığına uğrattı. Java programcıları için programlarından faydalanmanın kolay bir yolu yoktur invokedynamic
.
invokedynamic
Java programcıları için hiçbir zaman kullanılmak üzere tasarlanmamıştır . IMO Java felsefesine hiç uymuyor. Java dışındaki diller için bir JVM özelliği olarak eklendi.
İnfürodinamiğe devam etmeden önce anlaşılması gereken iki kavram vardır.
1. Statik ve Dinamin Yazımı
Statik - derleme zamanında tür denetimini önceden gerçekleştirir (örn. Java)
Dinamik - çalışma zamanında tür denetimini önceden gerçekleştirir (ör. JavaScript)
Tür denetimi, bir programın tür güvenli olduğunu doğrulama işlemidir; bu, sınıf ve örnek değişkenleri, yöntem parametreleri, dönüş değerleri ve diğer değişkenler için yazılan bilgileri denetleme işlemidir. Java, derleme zamanında int, String, .. hakkında bilgi sahibi olurken, JavaScript'teki bir nesnenin türü yalnızca çalışma zamanında belirlenebilir
2. Güçlü ve Zayıf Yazma
Güçlü - işlemlerine sağlanan değer türleri üzerindeki kısıtlamaları belirtir (örn. Java)
Zayıf - bu bağımsız değişkenlerin uyumsuz türleri varsa (örneğin, Visual Basic) bir işlemin bağımsız değişkenlerini dönüştürür
Java'nın Statik ve Zayıf yazıldığını bilerek, JVM'ye Dinamik ve Güçlü yazılan dilleri nasıl uygularsınız?
Invokedynamic, program derlendikten sonra bir yöntem veya işlevin en uygun uygulamasını seçebilen bir çalışma zamanı sistemi uygular.
Örnek: (a + b) 'ye sahip olmak ve derleme zamanında a, b değişkenleri hakkında hiçbir şey bilmemek, invokedynamic bu işlemi çalışma zamanında Java'daki en uygun yöntemle eşler. Örnegin, a ortaya çikarsa, b Dizelerdir, sonra yöntemi (Dize a, Dize b) çagirin. A ortaya çıkarsa, b ints, sonra yönteme çağırın (int a, int b).
invokedynamic, Java 7 ile tanıtıldı.
Java Records makalemin bir parçası olarak , Inoke Dynamic'in arkasındaki motivasyon hakkında konuştum . Indy'nin kaba bir tanımıyla başlayalım.
Invoke Dynamic ( Indy olarak da bilinir ), Dinamik Tür Diller için JVM desteğini geliştirmek isteyen JSR 292'nin bir parçasıydı . Java 7'deki ilk sürümünün ardından, invokedynamic
opcode, java.lang.invoke
bagajıyla birlikte JRuby gibi dinamik JVM tabanlı diller tarafından oldukça yaygın olarak kullanılmaktadır.
Indy, dinamik dil desteğini geliştirmek için özel olarak tasarlanmış olsa da, bundan çok daha fazlasını sunar. Nitekim, bir dil tasarımcısının dinamik tip akrobasiden dinamik stratejilere kadar herhangi bir dinamikliğe ihtiyaç duyduğu her yerde kullanmak uygundur!
Örneğin, Java 8 Lambda İfadeleri, invokedynamic
Java statik olarak yazılmış bir dil olmasına rağmen, aslında kullanılarak uygulanır !
JVM oldukça uzun bir süredir dört yöntem çağırma türünü desteklemiştir: invokestatic
statik yöntemleri invokeinterface
çağırmak, arayüz yöntemlerini invokespecial
çağırmak, yapıcıları super()
veya özel yöntemleri invokevirtual
çağırmak ve örnek yöntemlerini çağırmak.
Farklılıklarına rağmen, bu çağırma türleri ortak bir özelliği paylaşır: onları kendi mantığımızla zenginleştiremeyiz . Aksine, invokedynamic
çağırma sürecini istediğimiz şekilde önyüklememizi sağlar. Ardından JVM, doğrudan Bootstrapped Yöntemini çağırmaya özen gösterir.
JVM ilk kez bir invokedynamic
komut gördüğünde , Bootstrap Yöntemi adlı özel bir statik yöntem çağırır . Bootstrap yöntemi, çağrılacak gerçek mantığı hazırlamak için yazdığımız bir Java kodu parçasıdır:
Sonra bootstrap yöntemi bir örneğini döndürür java.lang.invoke.CallSite
. Bu CallSite
, gerçek yönteme bir referans tutar, yani MethodHandle
.
Şu andan itibaren, JVM bu invokedynamic
talimatı her gördüğünde Yavaş Yolu atlar ve doğrudan temeldeki yürütülebilir dosyayı çağırır. Bir şey değişmedikçe JVM yavaş yolu atlamaya devam eder.
Java 14 Records
, aptal veri sahipleri olması gereken sınıfları bildirmek için güzel bir kompakt sözdizimi sağlıyor.
Bu basit kayıt göz önüne alındığında:
public record Range(int min, int max) {}
Bu örneğin bayt kodu şuna benzer:
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
Onun içinde özyükleme Yöntem Tablo :
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
Sınıfta bulunan Kayıtlar için bootstrap yöntemi çağrılır . Gördüğünüz gibi, bu bootstrap yöntemi aşağıdaki parametreleri bekler:bootstrap
java.lang.runtime.ObjectMethods
MethodHandles.Lookup
Arama bağlamını temsil eden bir örnek ( Ljava/lang/invoke/MethodHandles$Lookup
Bölüm).toString
, equals
, hashCode
vs.) önyükleme linke gidiyor. Örneğin, değer olduğunda toString
, bootstrap bu Kayıt için gerçek uygulamaya işaret ConstantCallSite
eden bir ( CallSite
asla değişmeyen) döndürür toString
.TypeDescriptor
Yöntem için ( Ljava/lang/invoke/TypeDescriptor
bir bölümü).Class<?>
Kayıt sınıfı türünü temsil eden bir tür belirteci . Bu var
Class<Range>
bu durumda.min;max
.MethodHandle
bileşenin başına. Bu şekilde bootstrap yöntemi, MethodHandle
bu özel yöntem uygulaması için bileşenlere dayalı bir temel oluşturabilir .invokedynamic
Kullanıcı önyükleme yöntemi için tüm bu bağımsız değişkenler geçirir. Bootstrap yöntemi de bunun bir örneğini döndürür ConstantCallSite
. Bu ConstantCallSite
, talep edilen yöntem uygulamasına bir referans tutarken, örn toString
.
Reflection API'larının aksine java.lang.invoke
, JVM tüm çağrıları tamamen görebildiğinden API oldukça etkilidir. Bu nedenle, JVM yavaş yoldan mümkün olduğunca kaçındığımız sürece her türlü optimizasyonu uygulayabilir!
Verimlilik argümanına ek olarak, invokedynamic
yaklaşım basitliği nedeniyle daha güvenilir ve daha az kırılgandır .
Ayrıca, Java Kayıtları için oluşturulan bayt kodu, özellik sayısından bağımsızdır. Böylece, daha az bayt kodu ve daha hızlı başlatma süresi.
Son olarak, yeni bir Java sürümünün yeni ve daha verimli bir önyükleme yöntemi uygulaması içerdiğini varsayalım. İle invokedynamic
, bizim app yeniden derleme olmadan bu gelişme yararlanabilirsiniz. Bu şekilde bir tür İleri İkili Uyumluluğa sahibiz . Ayrıca, bahsettiğimiz dinamik strateji bu!
Java Kayıtlarına ek olarak, invoke dinamik aşağıdaki gibi özellikleri uygulamak için kullanılmıştır:
LambdaMetafactory
StringConcatFactory
meth.invoke(args)
. Peki nasılinvokedynamic
uyuyormeth.invoke
?