2016-01-21 16 views
6

(Đây là khó khăn để tìm kiếm vì kết quả là tất cả về "phương pháp tham khảo")Java 8: chuyển đổi lambda để một trường hợp với clousure Phương pháp bao gồm

Tôi muốn để có được một trường hợp Method cho một biểu thức lambda để sử dụng với một API dựa trên phản chiếu kế thừa. Các clousure nên được bao gồm, do đó, gọi thatMethod.invoke(null, ...) nên có tác dụng tương tự như gọi lambda.

Tôi đã xem MethodHandles.Lookup, nhưng dường như chỉ có liên quan đến biến đổi ngược lại. Nhưng tôi đoán phương pháp bind có thể giúp bao gồm cả clousure?

Edit:

Nói rằng tôi có am lambda experssion:

Function<String, String> sayHello = name -> "Hello, " + name; 

và tôi có một khuôn khổ di sản (SpEL) có một API như

registerFunction(String name, Method method) 

mà sẽ gọi cho Method không có đối số this (tức là Phương thức được giả định là tĩnh). Vì vậy, tôi sẽ cần để có được một ví dụ đặc biệt Method bao gồm logic lambda + dữ liệu clousure.

+0

Không có gì giống như một thể hiện 'Method' cho biểu thức lambda. Lambda là cú pháp cho các hàm ẩn danh. Họ cung cấp cho bạn một ví dụ, nhưng bạn không thể tạo ra một phương thức ra khỏi nó – Jatin

+0

@Jatin Rõ ràng, sự suy giảm giao diện chức năng tổng hợp vẫn chỉ là một đối tượng bình thường với các phương thức bình thường, mà bạn có thể sử dụng sự phản chiếu bình thường để truy cập. Tôi chỉ tự hỏi làm thế nào để có được clousure được bao bọc là tốt. –

Trả lời

7

Trong trường hợp bạn không tìm thấy một cách thanh lịch, đây là cách xấu xí (Ideone). cảnh báo thông thường khi phản ánh có liên quan: có thể phá vỡ trong phiên bản tương lai, vv

public static void main(String[] args) throws Exception { 
    Function<String, String> sayHello = name -> "Hello, " + name; 
    Method m = getMethodFromLambda(sayHello); 
    registerFunction("World", m); 
} 

static void registerFunction(String name, Method method) throws Exception { 
    String result = (String) method.invoke(null, name); 
    System.out.println("result = " + result); 
} 

private static Method getMethodFromLambda(Function<String, String> lambda) throws Exception { 
    Constructor<?> c = Method.class.getDeclaredConstructors()[0]; 
    c.setAccessible(true); 
    Method m = (Method) c.newInstance(null, null, null, null, null, 0, 0, null, null, null, null); 
    m.setAccessible(true); //sets override field to true 

    //m.methodAccessor = new LambdaAccessor(...) 
    Field ma = Method.class.getDeclaredField("methodAccessor"); 
    ma.setAccessible(true); 
    ma.set(m, new LambdaAccessor(array -> lambda.apply((String) array[0]))); 

    return m; 
} 

static class LambdaAccessor implements MethodAccessor { 
    private final Function<Object[], Object> lambda; 
    public LambdaAccessor(Function<Object[], Object> lambda) { 
    this.lambda = lambda; 
    } 

    @Override public Object invoke(Object o, Object[] os) { 
    return lambda.apply(os); 
    } 
} 
+3

Tôi chào mừng bạn vì tin tặc tuyệt vời này! –

+0

Tôi đã xem xét các khả năng trong thuộc tính 'MethodHandle' và có vẻ như tốt nhất nó có thể làm là tạo ra các phương thức cá thể, do đó một kết thúc chết ở đây do yêu cầu động. –

+1

Phần hài hước nhất là cách bạn làm cho 'm' có thể truy cập được; thay vì gọi 'setAccessible (true)' trên nó, bạn lấy trường phụ trợ 'override' để gọi' setAccessible (true) 'trên' Field' đó để sử dụng nó sau đó để đặt nó thành 'true'. Tôi đoán, đó là điều sẽ xảy ra, nếu bạn sử dụng 'setAccessible (true)' quá thường xuyên ... – Holger

4

Vâng, biểu thức lambda được khử đường vào phương pháp trong biên soạn và chừng nào họ không nắm bắt this (không truy cập không static thành viên), các phương pháp này sẽ là static. Phần khó khăn là để có được các phương thức này vì không có kết nối có thể kiểm tra giữa thể hiện giao diện chức năng và phương thức đích của nó.

Để minh họa điều này, đây là trường hợp đơn giản nhất:

public class LambdaToMethod { 
    public static void legacyCaller(Object arg, Method m) { 
     System.out.println("calling Method \""+m.getName()+"\" reflectively"); 
     try { 
      m.invoke(null, arg); 
     } catch(ReflectiveOperationException ex) { 
      ex.printStackTrace(); 
     } 
    } 
    public static void main(String[] args) throws URISyntaxException 
    { 
     Consumer<String> consumer=s -> System.out.println("lambda called with "+s); 
     for(Method m: LambdaToMethod.class.getDeclaredMethods()) 
      if(m.isSynthetic() && m.getName().contains("lambda")) { 
       legacyCaller("a string", m); 
       break; 
      } 
    } 
} 

này hoạt động trơn tru như chỉ có một biểu thức lambda và do đó, một phương pháp ứng cử viên. Tên của phương pháp đó là trình biên dịch cụ thể và có thể chứa một số số serial hoặc mã băm vv

On kludge là làm cho biểu thức lambda serializable và kiểm tra hình thức tuần tự của nó:

static Method lambdaToMethod(Serializable lambda) { 
    for(Class<?> cl=lambda.getClass(); cl!=null; cl=cl.getSuperclass()) try { 
     Method m=cl.getDeclaredMethod("writeReplace"); 
     m.setAccessible(true); 
     try { 
      SerializedLambda sl=(SerializedLambda)m.invoke(lambda); 
      return LambdaToMethod.class.getDeclaredMethod(sl.getImplMethodName(), 
       MethodType.fromMethodDescriptorString(sl.getImplMethodSignature(), 
        LambdaToMethod.class.getClassLoader()).parameterArray()); 
     } catch(ReflectiveOperationException ex) { 
      throw new RuntimeException(ex); 
     } 
    } catch(NoSuchMethodException ex){} 
    throw new AssertionError(); 
} 
public static void main(String[] args) 
{ 
    legacyCaller("a string", lambdaToMethod((Consumer<String>&Serializable) 
     s -> System.out.println("first lambda called with "+s))); 
    legacyCaller("a string", lambdaToMethod((Consumer<String>&Serializable) 
     s -> System.out.println("second lambda called with "+s))); 
} 

này hoạt động, tuy nhiên lambdas tuần tự có giá cao.


Giải pháp đơn giản nhất sẽ có thêm một chú thích để một tham số của biểu thức lambda để được tìm thấy khi lặp lại so với phương pháp này, tuy nhiên, hiện nay, javac không lưu trữ các chú thích đúng cách, xem thêm this question về vấn đề này đề tài.


Nhưng bạn cũng có thể xem xét việc tạo các phương thức bình thường static giữ mã thay vì biểu thức lambda.Lấy đối tượng Method cho một phương thức thẳng về phía trước và bạn vẫn có thể tạo một thể hiện giao diện chức năng trong số chúng bằng cách sử dụng tham chiếu phương thức…

Các vấn đề liên quan