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.
invokedynamicperformans 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.
invokedynamicJava 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, invokedynamicopcode, java.lang.invokebagajı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, invokedynamicJava 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: invokestaticstatik 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 invokedynamickomut 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 invokedynamictalimatı 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:bootstrapjava.lang.runtime.ObjectMethods
MethodHandles.LookupArama bağlamını temsil eden bir örnek ( Ljava/lang/invoke/MethodHandles$LookupBölüm).toString, equals, hashCodevs.) önyükleme linke gidiyor. Örneğin, değer olduğunda toString, bootstrap bu Kayıt için gerçek uygulamaya işaret ConstantCallSiteeden bir ( CallSiteasla değişmeyen) döndürür toString.TypeDescriptorYö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.MethodHandlebileşenin başına. Bu şekilde bootstrap yöntemi, MethodHandlebu özel yöntem uygulaması için bileşenlere dayalı bir temel oluşturabilir .invokedynamicKullanı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, invokedynamicyaklaşı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:
LambdaMetafactoryStringConcatFactory
meth.invoke(args). Peki nasılinvokedynamicuyuyormeth.invoke?