2014-11-06 18 views
16

Açıkça LambdaMetafactory.metafactory kullanmaya çalışıyorum, neden sadece Runnable işlevsel arabirimiyle çalıştığını anlayamıyorum. Böyle Tedarikçi gibi farklı bir fonksiyonel bir arayüz kullanmaya çalıştığınızdaAçıkça kullanımı LambdaMetafactory

public class MetafactoryTest { 

    public static void main(String[] args) throws Throwable { 

     MethodHandles.Lookup caller = MethodHandles.lookup(); 
     MethodType methodType = MethodType.methodType(void.class); 
     MethodType invokedType = MethodType.methodType(Runnable.class); 
     CallSite site = LambdaMetafactory.metafactory(caller, 
                 "run", 
                 invokedType, 
                 methodType, 
                 caller.findStatic(MetafactoryTest.class, "print", methodType), 
                 methodType); 
     MethodHandle factory = site.getTarget(); 
     Runnable r = (Runnable) factory.invoke(); 
     r.run(); 
    } 

    private static void print() { 
     System.out.println("hello world"); 
    }  
} 

sorun ortaya çıkar: Örnek için bu kod (Bu "merhaba dünya" yazdırır) ne beklendiğini yapar. Aşağıdaki kod çalışmaz:

public class MetafactoryTest { 

    public static void main(String[] args) throws Throwable { 

     MethodHandles.Lookup caller = MethodHandles.lookup(); 
     MethodType methodType = MethodType.methodType(String.class); 
     MethodType invokedType = MethodType.methodType(Supplier.class); 
     CallSite site = LambdaMetafactory.metafactory(caller, 
                 "get", 
                 invokedType, 
                 methodType, 
                 caller.findStatic(MetafactoryTest.class, "print", methodType), 
                 methodType); 
     MethodHandle factory = site.getTarget(); 
     Supplier<String> r = (Supplier<String>) factory.invoke(); 
     System.out.println(r.get());   
    } 
    private static String print() { 
     return "hello world"; 
    }  
} 


Exception in thread "main" java.lang.AbstractMethodError: metafactorytest.MetafactoryTest$$Lambda$1/258952499.get()Ljava/lang/Object; 
    at metafactorytest.MetafactoryTest.main(MetafactoryTest.java:29) 

olmamalı kodun ikinci ön bilgide sorundur benzer şekilde, kod işin iki pasajı?

Ayrıca aşağıdaki kod, bu eşdeğer olmalıdır, iyi çalışır:

public class MetafactoryTest { 

    public static void main(String[] args) throws Throwable { 
     Supplier<String> r = (Supplier<String>)() -> print(); 
     System.out.println(r.get());   
    } 

    private static String print() { 
     return "hello world"; 
    }  
} 

Düzen

yöntem dönüş türü yeni bir fonksiyonel bir arayüz tanımlamaktır değiştirmek önler Başka bir çözüm:

public class MetafactoryTest { 

    @FunctionalInterface 
    public interface Test { 
     String getString(); 
    } 

    public static void main(String[] args) throws Throwable { 

     MethodHandles.Lookup caller = MethodHandles.lookup(); 
     MethodType methodType = MethodType.methodType(String.class); 
     MethodType invokedType = MethodType.methodType(Test.class); 
     CallSite site = LambdaMetafactory.metafactory(caller, 
                 "getString", 
                 invokedType, 
                 methodType, 
                 caller.findStatic(MetafactoryTest.class, "print", methodType), 
                 methodType); 
     MethodHandle factory = site.getTarget(); 
     Test r = (Test) factory.invoke(); 
     System.out.println(r.getString());   
    } 

    private static String print() { 
     return "hello world"; 
    } 
+2

Belki de sorun, ikinci argüman olarak geçtiğiniz "run" yöntem adıyla ilgilidir. Runnable bir "run" yöntemine sahiptir. Tedarikçi yok. – Eran

+0

Bu bir hataydı (Runnable durumu yalnızca "run" ile çalışır), ancak ikinci snippet'i de bu hataya sahip olsun. – andrebask

cevap

14

Runnable ve Tedarikçi arasındaki fark, Tedarikçi'nin jenerik tip kullanmasıdır.

Çalışma zamanında Tedarikçi bir String get() yöntemine sahip değil, Object get() içeriyor. Ancak uyguladığınız yöntem bir String döndürür. Bu 2 tip arasında ayrım yapmalısınız. LambdaMetafactory sonra hedef arayüzü oluşturmak için kullanılan bir sentetik fabrika sınıfını oluşturduğundan callSiteType bir vardır,

public class Demo { 
    public static void main(String[] args) throws Throwable { 
     Consumer<String> consumer = s -> System.out.println("CONSUMED: " + s + "."); 

     consumer.accept("foo"); 

     MethodHandles.Lookup caller = MethodHandles.lookup(); 

     MethodType lambdaBodyMethodType = MethodType.methodType(void.class, String.class); 
     MethodHandle lambdaBody = caller.findStatic(
       Demo.class, "my$lambda$main$0", lambdaBodyMethodType); 

     // Because of the type erasure we must use Object here 
     // instead of String (Consumer<String> -> Consumer). 
     MethodType functionalInterfaceMethodType = 
       MethodType.methodType(void.class, Object.class); 

     // we must return consumer, no closure -> no additional parameters 
     MethodType callSiteType = MethodType.methodType(Consumer.class); 

     CallSite site = LambdaMetafactory.metafactory(
       // provided by invokedynamic: 
       caller, "accept", callSiteType, 
       // additional bootstrap method arguments: 
       functionalInterfaceMethodType, 
       lambdaBody, 
       lambdaBodyMethodType); 

     MethodHandle factory = site.getTarget(); 
     Consumer<String> r = (Consumer<String>) factory.invoke(); 

     r.accept("foo"); 
     r.accept("bar"); 
    } 

    private static void my$lambda$main$0(String s) { 
     System.out.println("CONSUMED: " + s + "."); 
    } 
} 

: Bu gibi:

public class MetafactoryTest { 

    public static void main(String[] args) throws Throwable { 

     MethodHandles.Lookup caller = MethodHandles.lookup(); 
     MethodType methodType = MethodType.methodType(Object.class); 
     MethodType actualMethodType = MethodType.methodType(String.class); 
     MethodType invokedType = MethodType.methodType(Supplier.class); 
     CallSite site = LambdaMetafactory.metafactory(caller, 
                 "get", 
                 invokedType, 
                 methodType, 
                 caller.findStatic(MetafactoryTest.class, "print", actualMethodType), 
                 methodType); 
     MethodHandle factory = site.getTarget(); 
     Supplier<String> r = (Supplier<String>) factory.invoke(); 
     System.out.println(r.get()); 
    } 

    private static String print() { 
     return "hello world"; 
    }  
} 
+0

Baskı yöntemi bir argüman içeriyorsa bu işe yarar mı? –

+1

Baskı yönteminin bir argümana ihtiyacı varsa, "Çalışabilir" veya "Tedarikçi" arayüzünü uygulamak için kullanılamaz. –

+0

Kullanılabilecek herhangi bir şey var mı? Hangisini önerirsiniz? Benim örneğimde, bir boole döndüren ve bir veya daha fazla Dizeyi parametre olarak alan statik olmayan bir işlevi çağırmaya çalışıyorum. –

0

Bu değişken adlarını anlamak için daha kolay olan başka bir örnektir Bu fabrikanın türü create() yöntemidir. Bu create() yöntemi, örtülü olarak invokedynamic - LambdaMetafactory, oluşturma yöntemine bir yöntem başvurusu olan bir CallSite döndürür. Kapaklı lambdalar için factory.create(capturedValue1, ..., capturedValueN) gibi fabrikayı arayacaksınız ve buna göre callSiteType'u değiştirmelisiniz.