2015-07-02 20 views
6

Tôi muốn chụp các cuộc gọi đến một đối tượng giảLàm cách nào tôi có thể tìm thấy mục tiêu của tham chiếu phương thức Java8?

public interface Service { 
    public String stringify(Object o); 
} 
service = mockery.mock(Service.class); 
mockery.allowing(service::stringify).with(42).will(() -> "42"); 

Vì vậy, bên allowing Tôi có một Function<Object, String>

Có reflecto-kỳ diệu mà sẽ cho phép tôi tìm thấy những dịch vụ từ chức năng được tạo ra từ các tài liệu tham khảo phương pháp ?

public WithClause allowing(Function<T,R> f) { 
    Object myServiceBackAgain = findTargetOf(function); 
    .... 
} 

Tôi biết rằng Chức năng sẽ luôn đến từ các tham chiếu phương pháp này, vì vậy tôi rất vui khi được truyền xuống nhiều như cần thiết.

Đây không phải là câu hỏi giống như liên quan Is it possible to convert method reference to MethodHandle? bởi vì, tốt cho một sự khởi đầu nó không phải là cùng một câu hỏi, chỉ trong một khu vực có liên quan. Và ngay cả khi tôi có thể có được một MethodHandle, tôi không thể có được mục tiêu từ nó.

+0

Không ai nói rằng đây là cùng một câu hỏi. Có lẽ bạn nên đọc tin nhắn: “* Câu hỏi này đã có câu trả lời ở đây *”. Vì vậy, bắt đầu đọc câu trả lời *. Nó giải thích mọi thứ, vì vậy bạn không cần suy đoán xem liệu bạn có thể có được một MethodHandle hay không, đó không phải là vấn đề. Đó là * chỉ một tiêu đề *. – Holger

+0

Có lẽ @ Holger, có thể là câu trả lời cho câu hỏi này, bạn muốn giải thích cách câu trả lời cho câu hỏi được liên kết trả lời tôi? Đặc biệt, nơi chức năng không phải là serializable. –

+0

về cơ bản nó giải thích rằng nó không thể (ngoài thủ thuật serialization) và thậm chí nếu nó được, nó không làm những gì bạn mong đợi vì không có bảo đảm rằng một tham chiếu phương pháp trong mã nguồn của bạn kết thúc lên là một xử lý trực tiếp trên byte mức mã. Lưu ý thêm, có rất nhiều liên kết đến các câu hỏi liên quan để biết thêm thông tin. Đó là lý do tại sao tôi hướng đến câu hỏi đó thay vì trực tiếp đến ["Làm thế nào để có được MethodInfo của một tham chiếu phương pháp Java 8?"] (Http://stackoverflow.com/q/19845213/2711488) gần gũi hơn với từ ngữ của bạn nhưng cung cấp ít thông tin hơn trong các câu trả lời. – Holger

Trả lời

4

Sử dụng mẹo từ this SO post bạn có thể tìm thấy mục tiêu. Phương pháp quan trọng bên dưới là findTarget. Khi nó quay ra, lambdas thực sự nắm bắt các mục tiêu của họ, và bạn có thể truy cập chúng từ SerializedLambda.

Tuy nhiên, đây là một sự phản ánh khá khó chịu và nó có khả năng phá vỡ trong các phiên bản sau. Tôi không tha thứ cho việc sử dụng nó.

import java.io.Serializable; 
import java.lang.invoke.SerializedLambda; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 
import java.util.Optional; 
import java.util.function.Function; 

public class FindMethodReferenceTarget { 
    public static void main(String[] args) { 
    String s = "123"; 
    Optional<Object> target = findTarget(s::charAt); 
    System.out.println(target.get().equals(s)); 

    Object o = new FindMethodReferenceTarget(); 
    target = findTarget(o::equals); 
    System.out.println(target.get().equals(o)); 
    } 

    private static <T, R> Optional<Object> findTarget(
     DebuggableFunction<T, R> methodReference) { 
    return getLambda(methodReference).map(l -> l.getCapturedArg(0)); 
    } 

    private static Optional<SerializedLambda> getLambda(Serializable lambda) { 
    for (Class<?> cl = lambda.getClass(); cl != null; cl = cl.getSuperclass()) { 
     try { 
     Method m = cl.getDeclaredMethod("writeReplace"); 
     m.setAccessible(true); 
     Object replacement = m.invoke(lambda); 
     if (!(replacement instanceof SerializedLambda)) { 
      break; // custom interface implementation 
     } 
     SerializedLambda l = (SerializedLambda) replacement; 
     return Optional.of(l); 
     } catch (NoSuchMethodException e) { 
     // do nothing 
     } catch (IllegalAccessException | InvocationTargetException e) { 
     break; 
     } 
    } 

    return Optional.empty(); 
    } 

    @FunctionalInterface 
    private static interface DebuggableFunction<T, R> extends 
     Serializable, 
     Function<T, R> {} 
} 
0

Không có cách nào trực tiếp để tìm mục tiêu, bởi vì các tham chiếu phương pháp chỉ được dịch sang lambdas (theo định nghĩa, ẩn danh) trong trang bìa. Vì vậy, bạn sẽ cần phải sử dụng một cách giải quyết.

Có lẽ bạn đã quen thuộc với proxy của Java 7, vì bạn đã quản lý để triển khai phương pháp nhà máy mock của mình.

Cách giải quyết sau đó là khi ai đó gọi phương pháp allowing của bạn, bạn thiết lập một số loại cờ toàn cầu để cảnh báo tất cả mocks của bạn mà bạn muốn ghi lại các cuộc gọi tiếp theo, và sau đó bạn gọi lambda bạn đã đưa ra. Bằng cách xem mô hình nào đã ghi lại cuộc gọi, bây giờ bạn đã tìm thấy mục tiêu của tham chiếu phương thức và bạn có thể bỏ đặt cờ toàn cầu và tiếp tục với phần còn lại của khuôn khổ mocking của bạn.

Thật xấu, tôi biết.

+0

Vâng, đó là những gì tôi đang làm vào lúc này! Tôi vẫn hy vọng rằng ẩn danh chỉ áp dụng nếu bạn không biết những gì để xuống diễn viên quá - sau khi tất cả việc thực hiện giao diện là một điều cụ thể mà phải biết đó là mục tiêu. –

+0

@DuncanMcGregor không nhất thiết; JVM có thể thực thi lambda như là một đóng (và không phải là một phương thức trên một số đối tượng) trong đó thể hiện của 'Service' hoạt động như một bộ thu cơ bản là một biến cục bộ từ phối cảnh thực hiện lambda (và không có cách nào để quan sát các biến cục bộ của một hàm tùy ý từ bên ngoài hàm đó). Trong trường hợp xấu nhất, hàm có thể không phải là java mà thay vào đó là một cuộc gọi đến mã nguồn gốc (ví dụ: C). Tôi không biết đó là những gì JVM thực sự làm, nhưng tôi nghĩ họ đã thiết lập API/hợp đồng để họ có thể thực hiện việc này. –

+0

Tôi đồng ý rằng có nhiều cách mà một Lambda có thể được triển khai - nhưng trong trường hợp này, JVM phải giữ một tham chiếu đến đối tượng để thực hiện cuộc gọi ngay? –

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