2015-06-03 18 views
7

Khi tạo lambda theo cách thủ công, hãy sử dụng MethodHandles.Lookup, MethodHandle s, MethodType s, v.v ... làm cách nào để có thể triển khai chụp biến?Capture biến dạng Lambda Metafactory

Ví dụ, không có chụp:

public IntSupplier foo() { 
    return this::fortyTwo; 
} 
/** 
* Would not normally be virtual, but oh well. 
*/ 
public int fortyTwo() { 
    return 42; 
} 

và hình thức clunkier của nó, sử dụng công cụ trong java.lang.invoke:

public IntSupplier foo() { 
    MethodHandles.Lookup lookup = MethodHandles.lookup(); 
    MethodType methodType = MethodType.methodType(int.class), 
       lambdaType = MethodType.methodType(IntSupplier.class); 
    MethodHandle methodHandle = lookup.findVirtual(getClass(), "fortyTwo", methodType); 
    CallSite callSite = LambdaMetafactory.metafactory(lookup, "getAsInt", lambdaType, methodType, methodHandle, methodType); 
    return (IntSupplier) callSite.getTarget().invokeExact(); 
} 
/** 
* Would not normally be virtual, but oh well. 
*/ 
public int fortyTwo() { 
    return 42; 
} 

sẽ trở lại một cách đơn giản, vô nghĩa IntSupplier trả 42 khi gọi, nhưng những gì nếu ai muốn chụp một cái gì đó?

+0

Tôi sẽ giả định điều này là nhằm mục đích tìm hiểu cách lambdas làm việc nội bộ, bởi vì nếu không bạn sẽ 'trả về IntSupplier mới() {...};' không có phép thuật. – immibis

+1

Điều tôi không hiểu, là nhận xét “Sẽ không bình thường ảo”. – Holger

Trả lời

3

Đối số thứ ba với phương pháp bootstrap, mà bạn tên lambdaType, là gọi loại của liên invokedynamic lệnh (thường được điền bởi JVM). Đó là ngữ nghĩa được định nghĩa bởi phương thức bootstrap và trong trường hợp của LambdaMetaFactory, nó chỉ định giao diện chức năng như kiểu trả về (kiểu đối tượng để xây dựng) và các giá trị để nắm bắt như kiểu tham số (kiểu giá trị cần dùng khi xây dựng một cá thể lambda).

Vì vậy, để nắm bắt this, bạn phải thêm các loại this để loại gọi của bạn và vượt qua this như một cuộc tranh cãi với invokeExact gọi:

public class Test { 
    public static void main(String... arg) throws Throwable { 
     System.out.println(new Test().foo().getAsInt()); 
    } 
    public IntSupplier foo() throws Throwable { 
     MethodHandles.Lookup lookup = MethodHandles.lookup(); 
     MethodType methodType = MethodType.methodType(int.class), 
        invokedType = MethodType.methodType(IntSupplier.class, Test.class); 
     MethodHandle methodHandle = lookup.findVirtual(getClass(), "fortyTwo", methodType); 
     CallSite callSite = LambdaMetafactory.metafactory(lookup, "getAsInt", 
      invokedType, methodType, methodHandle, methodType); 
     return (IntSupplier) callSite.getTarget().invokeExact(this); 
    } 
    public int fortyTwo() { 
     return 42; 
    } 
} 

Nếu bạn muốn chụp nhiều giá trị, bạn phải thêm chúng vào chữ ký theo đúng thứ tự. Ví dụ: để chụp khác int giá trị:

public class Test { 
    public static void main(String... arg) throws Throwable { 
     System.out.println(new Test().foo(100).getAsInt()); 
    } 
    public IntSupplier foo(int capture) throws Throwable { 
     MethodHandles.Lookup lookup = MethodHandles.lookup(); 
     MethodType methodType = MethodType.methodType(int.class, int.class), 
      functionType = MethodType.methodType(int.class), 
      invokedType = MethodType.methodType(IntSupplier.class, Test.class, int.class); 
     MethodHandle methodHandle=lookup.findVirtual(getClass(),"addFortyTwo",methodType); 
     CallSite callSite = LambdaMetafactory.metafactory(lookup, "getAsInt", 
      invokedType, functionType, methodHandle, functionType); 
     return (IntSupplier) callSite.getTarget().invokeExact(this, capture); 
    } 
    public int addFortyTwo(int valueToAdd) { 
     return 42+valueToAdd; 
    } 
} 

Phương pháp mục tiêu sẽ có một chữ ký bao gồm các loại this, nếu không muốn nói static, tiếp theo là tất cả các loại tham số. Giá trị chụp sẽ ánh xạ theo loại chữ ký này từ trái sang phải và các loại thông số còn lại, nếu có, đóng góp vào chữ ký chức năng, do đó phải khớp với các loại tham số của phương thức interface.

Điều này ngụ ý rằng khi không có giá trị bị bắt và phương pháp đích không phải là static, loại bộ tiếp nhận phương thức có thể trở thành kết hợp với loại chữ ký đầu tiên, như trong ToIntFunction<String> f=String::length;.

2

Mã của bạn sẽ không chạy. Vì phương pháp fortyTwo của bạn không tĩnh, bạn sẽ phải chụp this bằng cách sử dụng MethodType.methodType(IntSupplier.class, getClass()) làm đối số thứ 3 cho metafactory và sau đó chuyển this làm đối số cho invokeExact.

Dưới đây là một ví dụ về chụp một chuỗi sử dụng một phương pháp tĩnh để giữ cho mọi thứ đơn giản hơn:

public static int len(String s) { 
    return s.length(); 
} 

public IntSupplier supplyLength(String capture) throws Throwable { 
    MethodHandles.Lookup lookup = MethodHandles.lookup(); 

    CallSite callSite = LambdaMetafactory.metafactory(
      lookup, 
      "getAsInt", 
      methodType(IntSupplier.class, String.class), 
      methodType(int.class), 
      lookup.findStatic(getClass(), "len", methodType(int.class, String.class)), 
      methodType(int.class) 
    ); 

    return (IntSupplier) callSite.getTarget().invoke(capture); 
} 
+0

Tôi đã quên rằng 'điều này' cần phải được chụp ^^; cảm ơn vì đã nhắc nhở –

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