2016-07-20 24 views
8

Tôi muốn tạo hàm lambda trong Java 8, lấy tên lớp và sau đó khởi tạo lại hàm từ tên lớp của nó.Khởi tạo hàm Java lambda theo tên

Đây là những gì tôi cố gắng:

import java.util.function.Consumer; 

public class SimpleLambda 
{ 
    public static void call(String aLambdaClassName, String aArg) throws Exception 
    { 
     Class<Consumer<String>> lClass = (Class<Consumer<String>>) Class.forName(aLambdaClassName); 
     Consumer<String> newlamba = lClass.newInstance(); 
     newlamba.accept(aArg); 
    } 

    public static void main(String[] args) throws Exception 
    { 
     { 
      // Attempt with a static method as lambda 
      Consumer<String> lambda = Host::action; 
      String classname = lambda.getClass().getName(); 
      call(classname, "Hello world"); 
     } 

     { 
      // Attempt with a locally defined lambda 
      Consumer<String> lambda = (s) -> { System.out.println(s); }; 
      String classname = lambda.getClass().getName(); 
      call(classname, "Hello world"); 
     } 
    } 
} 

class Host { 
    public static void action(String aMessage) { 
     System.out.println(aMessage); 
    } 
} 

Tuy nhiên, với mã này (ở cả hai phiên bản, sử dụng tài liệu tham khảo phương pháp tĩnh và sử dụng lambda tại địa phương tuyên bố), tôi nhận được một ngoại lệ:

Exception in thread "main" java.lang.ClassNotFoundException: mypackage.SimpleLambda$$Lambda$1/471910020 
    at java.lang.Class.forName0(Native Method) 
    at java.lang.Class.forName(Class.java:264) 
    at mypackage.SimpleLambda.main(SimpleLambda.java:12) 

Tôi đã có thể mong đợi rằng tại ít nhất tôi có thể tái tạo lại tham chiếu phương pháp tĩnh ... không, dường như không.

Tôi đã sử dụng cách tiếp cận tương tự với Groovy Closures và hoạt động tốt. Vì vậy, tôi chỉ làm điều gì đó sai trái với Java 8 lambdas, hoặc là nó không thể khởi tạo lambda theo tên? Tôi tìm thấy một số gợi ý trên mạng mà lambdas có thể được (de) tuần tự hóa, vì vậy tôi hy vọng nó cũng nên có thể nhanh chóng chúng theo tên.

+2

Bạn đang cố gắng làm gì với họ? Lambdas không được khởi tạo, chúng chỉ * là *. – 4castle

+0

Về cơ bản, tôi muốn xử lý một lambda giống như một lớp bên trong tĩnh với một hàm duy nhất. Tôi muốn vượt qua nó cho mỗi tên lớp bởi vì tôi cần phải vượt qua nó thông qua một API mà chỉ chấp nhận tham số String.Vì vậy, một khi tôi nhận được tên bên trong API, tôi muốn tạo lại lambda một lần nữa từ tên lớp của nó để tôi có thể gọi nó. Âm thanh lạ? Vâng, nó là ... nhưng như tôi đã nói: đã làm nó trong Groovy và hoạt động độc đáo. Tôi có cảm giác nó sẽ không hoạt động trong Java, nhưng nếu ai đó có một đầu mối làm thế nào để làm điều đó, sẽ thực sự tuyệt vời. – rec

+0

Tôi đã cập nhật ví dụ để làm cho việc sử dụng rõ ràng hơn. – rec

Trả lời

5

Vâng, đó là đặc tính đặc biệt của JRE/OpenJDK của Oracle để sử dụng "tên ẩn danh", không thể truy cập theo tên nào cả. Nhưng ngay cả khi không này, không có lý do tại sao điều này nên làm việc:

  • Class.forName(String) cố gắng để giải quyết các lớp thông qua người gọi ClassLoader. Vì vậy, ngay cả khi các biểu thức lambda được triển khai bằng các lớp thông thường, không thể truy cập được nếu được tải qua một khác nhau
  • Class.newInstance() chỉ hoạt động nếu có một hàm xây dựng no-arg public. Bạn không thể giả định rằng có một hàm tạo no-arg cũng không phải là public
  • Giả định rằng toàn bộ logic của hàm phải nằm trong một lớp đơn lẻ là sai. Ví dụ về số lượt truy cập sẽ là java.lang.reflect.Proxy sẽ tạo ra các triển khai interface ủy quyền cho số InvocationHandler. Việc cố gắng tạo lại proxy như vậy thông qua tên lớp của nó sẽ không thành công, bởi vì bạn cần chuyển phiên bản thực tế InvocationHandler tới hàm tạo của proxy. Về nguyên tắc, việc triển khai biểu thức lambda cụ thể JRE có thể sử dụng một mẫu tương tự

Xem xét các điểm trên, rõ ràng là bạn không thể nói rằng nó hoạt động với các lớp bên trong nói chung. Có rất nhiều ràng buộc bạn phải thực hiện cho điều đó.


Về serialization, nó hoạt động cho các biểu thức lambda serializable, bởi vì hình thức dai dẳng là hoàn toàn tách ra khỏi lớp thực hiện thời gian chạy, như described in this answer. Vì vậy, tên của lớp được tạo ra không được chứa trong biểu mẫu tuần tự hóa và đầu cuối deserializing có thể có một thời gian chạy hoàn toàn khác nhau.

0

Lưu trữ các cá thể lambda trong Bản đồ, được khóa trên tên cá thể. Bạn có thể làm cho bản đồ có sẵn trên toàn cầu máng một lớp bao bọc đơn (chỉ cần xem ra cho các vấn đề đồng bộ hóa).

class LambdaMap { 

    private HashMap<String, Consumer<String>> theMap; 

    private LambdaMap() { 
     theMap = new HashMap<>(); 
    } 

    private static class INSTANCE_HOLDER { 
     private static LambdaMap INSTANCE = new LambdaMap(); 
    } 

    public static LambdaMap getInstance() { 
     return INSTANCE_HOLDER.INSTANCE; 
    } 

    public Consumer<String> put(String key, Consumer<String> value) { 
     return theMap.put(key, value); 
    } 

    public static void Call(String aLambdaClassName, String aArg) { 
     Consumer<String> func = getInstance().theMap.get(aLambdaClassName); 
     if (func != null) { 
      func.accept(aArg); 
     } 
    } 

} 

class Host { 
    public static void action(String aMessage) { 
     System.out.println("Goodbye, " + aMessage); 
    } 
} 

public class GlobalLambdas { 

    public static void main(String[] args) { 
     LambdaMap.getInstance().put("print greeting", s -> { 
      System.out.println("Hello, " + s); 
     }); 
     LambdaMap.getInstance().put("print goodbye", Host::action); 

     LambdaMap.Call("print greeting", "John"); 
     LambdaMap.Call("print goodbye", "John"); 
    } 
} 

run: 
Hello, John 
Goodbye, John 
Các vấn đề liên quan