2015-06-03 18 views
7

जब एक लैम्ब्डा मैन्युअल का उपयोग कर MethodHandles.Lookup, MethodHandle रों, MethodType रों, आदि बनाने, कैसे एक चर पर कब्जा को लागू कर सकते हैं?लैम्ब्डा Metafactory चर कब्जा

उदाहरण के लिए, कोई कब्जा करने के साथ:

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

और उसके clunkier फार्म, सामान का उपयोग कर 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; 
} 

एक सरल, व्यर्थ IntSupplier कि 42 लौटाता है जब लागू हो जाएंगे लेकिन क्या अगर कोई कुछ कैप्चर करना चाहेगा?

+0

मुझे लगता है कि यह समझने के उद्देश्य से है कि लैम्बडा आंतरिक रूप से कैसे काम करते हैं, क्योंकि अन्यथा आप नए इंट्सप्लियर() {...}; 'बिना किसी जादू के' वापस लौटेंगे। – immibis

+1

जो मुझे समझ में नहीं आता है, वह "सामान्य रूप से आभासी नहीं होगा" टिप्पणी है। – Holger

उत्तर

3

बूटस्ट्रैप विधि है, जो आप lambdaType नामित करने के लिए तीसरा तर्क, लागू प्रकार जुड़े invokedynamic की है निर्देश (आमतौर पर जेवीएम द्वारा भरा जाता है)। यह अर्थात् बूटस्ट्रैप विधि द्वारा परिभाषित किया गया है और LambdaMetaFactory के मामले में, यह कार्यात्मक इंटरफ़ेस को रिटर्न प्रकार (ऑब्जेक्ट का निर्माण करने के प्रकार) के रूप में निर्दिष्ट करता है और पैरामीटर प्रकार के रूप में कैप्चर करने के लिए मान (मूल्यों का प्रकार कब उपभोग करता है एक लैम्ब्डा उदाहरण का निर्माण)।

तो आदेश this पर कब्जा करने में, आप अपने लागू प्रकार के this के प्रकार जोड़ सकते हैं और thisinvokeExact कॉल करने के लिए एक तर्क के रूप पारित करने के लिए है:

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

आप अधिक मान कैप्चर करना चाहते हैं, तो आप उन्हें सही क्रम में हस्ताक्षर में जोड़ना होगा। ,

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

लक्ष्य विधि this प्रकार से मिलकर एक हस्ताक्षर करना होगा static यदि नहीं, तो सभी पैरामीटर प्रकार के द्वारा पीछा किया: उदाहरण, एक और int मूल्य पर कब्जा करने की। कैप्चर वैल्यू इस हस्ताक्षर के प्रकार को बाएं से दाएं और शेष पैरामीटर प्रकारों के लिए मैप करेगा, यदि कोई हो, तो कार्यात्मक हस्ताक्षर में योगदान दें, इसलिए interface विधि के पैरामीटर प्रकारों से मेल खाना होगा।

यह दर्शाता है कि जब कोई कब्जा मूल्य नहीं होता है और लक्ष्य विधि static नहीं है, तो विधि रिसीवर प्रकार कार्यात्मक हस्ताक्षर के पहले प्रकार से संबद्ध हो सकता है, जैसा कि ToIntFunction<String> f=String::length; में है।

2

आपका कोड नहीं चलेंगे। चूंकि आपका fortyTwo विधि स्थिर नहीं है, आप metafactory को 3 तर्क के रूप में MethodType.methodType(IntSupplier.class, getClass()) का उपयोग कर और फिर thisinvokeExact लिए एक तर्क के रूप में उत्तीर्ण होकर this पर कब्जा करना होगा।

यहाँ एक स्थिर पद्धति का उपयोग करके चीजों को रखने के लिए एक स्ट्रिंग पर कब्जा करने का एक उदाहरण है सरल:

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

मैं भूल गया था कि 'इस' को कब्जा करने की आवश्यकता है ^^; याद दिलाने के लिए धन्यवाद –

संबंधित मुद्दे