2016-03-17 22 views
5

grafik DependencyFinder ve java-callgraph gibi kütüphaneler tarafından üretilen java çağrı yoluyla kayması, ben bir çift anlamını öğrendim

java derleyicisi vb anonim işlevlerin, iç sınıflar için adları oluşturmak öğrendim onları (yanılıyorsam lütfen düzeltin):

  • org.example.Bar$Fooorg.example.Bar bir iç sınıfı olan Foo atıfta

    .
  • org.example.Bar$1, org.example.Bar yöntemlerinden birinin içinde bildirilen bir anonim sınıf anlamına gelir.
  • org.example.Bar.lambda$spam$1(), org.example.Bar.spam() yönteminin içinde bildirilen bir lambda karşılık gelir.

Ancak, ben de buldum:

  1. org.example.Bar$$Lambda$2.args$1
  2. org.example.Bar$$Lambda$2.call()
  3. org.example.Bar$$Lambda$7.lambdaFactory$()
  4. org.example.Bar$$Lambda$7.get$Lambda()

dört isim üzerinde neyi ifade ediyor? Çift dolar ($$) ne anlama geliyor?

+0

Sadece bir ayırıcı, her şey olabilir. – biziclop

+3

Çift $$'nın sadece lambda ifadeleri için kullanılan özel "yeni sınıflar" için kullanıldığı açıktır. Yani, iç sınıfların isimlerini ayırmak için $ ve lambda-ifadesi temelli iç sınıfları $$ olarak kullanır. – GhostCat

+2

Güncelleme: Spesifikasyonda bir referans buldum: http://docs.oracle.com/javase/specs/jls/se8/jls8.pdf - okumak. – rickchristie

cevap

5

Lambda ifadeleri için sınıflar, javac değil, JRE tarafından çalışma zamanında oluşturulur. İsimleri tamamen belirtilmemiş ve herhangi bir adlandırma şemasına güvenemezsiniz.

Açıkçası, Oracle’ın şu anki JRE'si tanınabilir bir desene sahip. Tanımlayan sınıfın adına $$Lambda$n değerini ekler, oysa n, derlenmiş kodun herhangi bir özelliği yerine, çalışma zamanında oluşturma sırasını yansıtan artan bir sayıdır.

Aşağıdaki program ile doğrulayabilirsiniz:

public class Test { 
    public static void main(String... args) { 
     if(args.length==0) { 
      final boolean meFirst = Math.random()<0.5; 
      if(meFirst) { 
       Runnable r=Test::main; 
       System.out.println("first run:\t"+r.getClass()); 
      } 
      main("second run"); 
      if(!meFirst) { 
       Runnable r=Test::main; 
       System.out.println("first run:\t"+r.getClass()); 
      } 
     } 
     else { 
      Runnable r=Test::main; 
      System.out.println(args[0]+":\t"+r.getClass()); 
      if(args[0].equals("second run")) main("last run"); 
     } 
    } 
} 

rastgele meFirst bayrağının durumuna bağlı olarak, ya

first run: class Test$$Lambda$1 
second run: class Test$$Lambda$2 
last run: class Test$$Lambda$2 

veya

second run: class Test$$Lambda$1 
last run: class Test$$Lambda$1 
first run: class Test$$Lambda$2 

It yazdırır Oluşturulan ilk sınıfın her zamannumarasını aldığını gösterir. İlk main çağrısında veya üçüncü yöntem referansında başlatılan ilk iki yöntem referansından biri olup olmadığına bakılmaksızın, ilk yinelemede örneklenen. Ayrıca, 3. uygulama her zaman aynı yöntem referans ifadesiyle aynıdır (tüm ifadelerin hedefi aynı olduğu için ifade ifadesi aynıdır) ve sınıf yeniden kullanılır.

Sürümüne bağlı olarak, adların gerçekten önemli olmadığını belirten adlara eklenen /number gibi bir şey görebilirsiniz, çünkü bu sınıfların her biri başka bir benzersiz tanıtıcıya sahiptir ("anonim sınıflar" olarak adlandırılırlar) ClassLoader ve adıyla bulamıyorsunuz).

Bu sınıflar içinde args$n gibi alan adları n’th yakalanan değeri temsil eder. LambdaMetafactory, yakalanan değişkenlerin gerçek isimleri hakkında bilgi sahibi olmadığından, bu tür isimleri üretmek için başka seçeneği yoktur.


Ancak, söylendiği gibi, bu bir uygulama artefaktıdır. Her tanımlayıcı sınıftaki her bir oluşturma sitesi için yeni bir sınıf oluşturulduğu sürece böyle bir adlandırma düzenini sürdürmek mümkündür. Ancak, şartname, eşdeğer lambda ifadelerini (aynı şeyi yapan) ve yöntem referanslarını (aynı yöntemi hedefleyen) temsil eden sınıfların ve örneklerin keyfi olarak paylaşılmasına/yeniden kullanılmasına izin verdiğinden, her uygulama stratejisi ile böyle bir isimlendirme modeli mümkün değildir.