İnvürodinamik nedir ve nasıl kullanılır?


159

JVM'ye eklenen tüm yeni harika özellikleri duymaya devam ediyorum ve bu harika özelliklerden biri invokinamik. Bunun ne olduğunu ve Java'da yansıtıcı programlamayı nasıl daha kolay veya daha iyi hale getirdiğini bilmek istiyorum?

Yanıtlar:


165

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ı.


3
Günlük Java programlama yöntemlerin dinamik olarak çağrılması için kullanılan yansımayı görmek nadir değildir meth.invoke(args). Peki nasıl invokedynamicuyuyor meth.invoke?
David K.

1
Bahsettiğim 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.
Ernest Friedman-Hill


1
Java 8'in bazı lambdaları kullanarak 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.
Nader Ghanbari

2
Sadece küçük bir uyarı, 2008'deki blog yazısı umutsuzca modası geçmiş ve gerçek yayın durumunu yansıtmıyor (2011).
Holger

9

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.


41
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.
Mark Peters

5
@Mark Hiç kim tarafından tasarlanmamış? Java dili ünlülerinde net bir güç yapısı yok ya da iyi tanımlanmış bir kolektif "niyet" var gibi değil. Dil felsefesi gelince - bu oldukça mümkün olduğunu Neal Gafter (haini!) Bakınız açıklama: infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter
irreputable

3
@mark peters: invokedynamic aslında sadece doğrudan erişilemeyen java programcıları için de tasarlanmıştır. Java 8'in kapanması için temel oluşturur.
M Platvoet

2
@irreputable: Asla JSR katılımcıları tarafından tasarlanmamıştır. JSR adının "Java Platformunda Dinamik Olarak Yazılmış Dilleri Desteklemesi" olduğunu söylüyor. Java, dinamik olarak yazılmış bir dil değildir.
Mark Peters

5
@ M Platvoet: Kapanışlarla güncel kalmadım, ancak kesinlikle kapatmalar için mutlak bir gereklilik olmaz. Tartıştıkları başka bir seçenek de, anonim iç sınıflar için VM spesifikasyon değişikliği olmadan yapılabilecek sözdizimsel şekerlerin kapatılmasıydı. Ama benim açımdan, JSR'nin asla Java diline dinamik yazmayı getirmek için tasarlanmadığı, JSR'yi okursanız çok açıktı.
Mark Peters

4

İ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ı.


4

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.

Indy hakkında

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 !

Kullanıcı Tanımlı Bayt Kodu

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.

Indy Nasıl Çalışır?

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:

resim açıklamasını buraya girin

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.

Örnek: Java 14 Kayıtları

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).
  • Yöntem adı (yani 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.
  • Tüm bileşen adlarının noktalı virgülle ayrılmış listesi, ör min;max.
  • Bir 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.

Neden Indy?

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!

Diğer Örnekler

Java Kayıtlarına ek olarak, invoke dinamik aşağıdaki gibi özellikleri uygulamak için kullanılmıştır:

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.