2014-11-06 14 views
16

Tôi đang cố gắng sử dụng LambdaMetafactory.metafactory một cách rõ ràng, tôi không thể hiểu tại sao nó chỉ hoạt động với giao diện chức năng Runnable. Ví dụ: mã này thực hiện những gì được mong đợi (nó in "hello world"):Sử dụng rõ ràng 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"); 
    }  
} 

Sự cố phát sinh khi tôi cố gắng sử dụng giao diện chức năng khác, chẳng hạn như nhà cung cấp. Mã sau đây không hoạt động:

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) 

Không phải hai đoạn mã hoạt động theo cách tương tự, đó là vấn đề trong đoạn mã thứ hai?

Hơn nữa đoạn mã sau, bạn cần phải tương đương, hoạt động tốt:

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"; 
    }  
} 

Sửa

Một giải pháp mà tránh thay đổi loại phương thức hoàn trả là để xác định một giao diện chức năng mới:

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

Có thể vấn đề là với tên phương thức "chạy" bạn chuyển làm đối số thứ hai. Runnable có một phương thức "chạy". Nhà cung cấp không. – Eran

+0

Đó là lỗi (Trường hợp Runnable chỉ hoạt động với "chạy"), nhưng cũng với đoạn mã thứ hai cung cấp lỗi đó. – andrebask

Trả lời

14

Sự khác biệt giữa Runnable và Nhà cung cấp là Nhà cung cấp sử dụng loại chung.

Khi chạy Nhà cung cấp không có phương thức get() String, nó có Object get(). Nhưng phương thức bạn thực hiện trả về một String. Bạn cần phải phân biệt giữa hai loại đó. Như thế này:

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

Điều này có hiệu quả nếu phương pháp in có chứa một đối số? –

+1

Nếu phương thức in cần một đối số, nó không thể được sử dụng để triển khai giao diện "Runnable" hoặc "Supplier". –

+0

Có bất kỳ thứ gì có thể sử dụng được không? Bạn sẽ giới thiệu cái nào? Trong trường hợp của tôi, tôi đang cố gắng gọi một hàm không tĩnh trả về một boolean và nhận một hoặc nhiều Chuỗi như các tham số. –

0

Đây là một ví dụ với một dễ dàng hơn để hiểu tên biến:

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 + "."); 
    } 
} 

LambdaMetafactory tạo ra một lớp nhà máy tổng hợp mà sau đó được sử dụng để tạo giao diện mục tiêu, callSiteType có loại phương pháp này của nhà máy create(). Phương pháp create() này được gọi là ngầm bởi invokedynamic - LambdaMetafactory trả về một CallSite có phương pháp tham chiếu đến phương thức tạo. Đối với lambdas với đóng cửa, bạn sẽ gọi cho nhà máy như factory.create(capturedValue1, ..., capturedValueN) và vì vậy bạn phải sửa đổi callSiteType cho phù hợp.