2016-01-21 9 views
6

जावा 8: clousure के साथ एक विधि उदाहरण के लिए लैम्ब्डा परिवर्तित शामिल

मैं एक साथ प्रयोग के लिए एक लैम्ब्डा अभिव्यक्ति के लिए एक Method उदाहरण प्राप्त करना चाहते हैं (यह खोज करने के लिए है क्योंकि परिणाम सब के बारे में "विधि संदर्भ" कर रहे हैं मुश्किल है) विरासत प्रतिबिंब आधारित एपीआई। क्लोजर को शामिल किया जाना चाहिए, इसलिए thatMethod.invoke(null, ...) पर कॉल करना लैम्ब्डा को कॉल करने जैसा ही प्रभाव होना चाहिए।

मैंने MethodHandles.Lookup पर देखा है, लेकिन यह केवल रिवर्स ट्रांसफॉर्म के लिए प्रासंगिक प्रतीत होता है। लेकिन मुझे लगता है कि bind विधि क्लोजर को शामिल करने में मदद कर सकती है?

संपादित करें:

मैं हूँ लैम्ब्डा experssion है कहते हैं:

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

और मैं एक विरासत रूपरेखा है (SpEL) एक API है कि जैसे

registerFunction(String name, Method method) 

फोन करेगा जो दिया Method बिना this तर्क (यानी विधि स्थैतिक मानी जाती है)। तो मुझे एक विशेष Method उदाहरण प्राप्त करने की आवश्यकता होगी जिसमें लैम्ब्डा तर्क + क्लोजर डेटा शामिल है।

+0

लैम्ब्डा अभिव्यक्ति के लिए 'विधि' उदाहरण की तरह कुछ भी नहीं है। लैम्ब्डा अज्ञात कार्यों के लिए वाक्य रचनात्मक चीनी हैं। वे आपको एक उदाहरण देते हैं, लेकिन आप इसे – Jatin

+0

@Jatin से बाहर नहीं कर सकते हैं जाहिर है, संश्लेषित कार्यात्मक इंटरफेस इम्प्लेमेंटेशन अभी भी सामान्य तरीकों के साथ एक सामान्य वस्तु है, जिसे आप सामान्य प्रतिबिंब का उपयोग करने के लिए उपयोग कर सकते हैं। मैं बस सोच रहा हूं कि क्लोजर को लपेटने के तरीके को कैसे प्राप्त किया जाए। –

उत्तर

7

यदि आपको कोई सुरुचिपूर्ण तरीका नहीं मिल रहा है, तो यहां बदसूरत तरीका है (Ideone)। सामान्य चेतावनी जब प्रतिबिंब शामिल है: भावी रिलीज़ आदि

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

मैं आपको इस अद्भुत हैकरी के लिए सलाम करता हूं! –

+0

मैंने 'मेथडहैंडल' संपत्ति में संभावनाओं के लिए चारों ओर देखा और ऐसा लगता है कि यह कर सकता है कि यह उदाहरण के तरीकों को उत्पन्न कर सकता है, इसलिए डायनामिक आवश्यकता के कारण यहां एक मृत अंत है। –

+1

सबसे मजेदार हिस्सा है जिस तरह से आप 'एम' सुलभ बनाते हैं; उस पर 'setAccessible (true)' को कॉल करने के बजाय, आप 'फ़ील्ड' पर 'setAccessible (true)' को कॉल करने के लिए बैकएंड फ़ील्ड 'ओवरराइड' प्राप्त करते हैं, इसे बाद में 'सत्य' पर सेट करने के लिए इसका उपयोग करने के लिए। मुझे लगता है, यही होगा, अगर आप 'setAccessible (true)' का उपयोग अक्सर करते हैं ... – Holger

4

ठीक है, लैम्ब्डा भाव संकलन के दौरान तरीकों में desugared कर रहे हैं और जब तक वे this पर कब्जा नहीं है (उपयोग न कर लें गैर static सदस्यों में टूट सकता है), ये विधियां static होगी। मुश्किल तरीका इन विधियों को प्राप्त करना है क्योंकि कार्यात्मक इंटरफ़ेस उदाहरण और इसकी लक्ष्य विधि के बीच कोई निरीक्षण योग्य संबंध नहीं है।

इस सबसे सामान्य स्थिति दर्शाने के लिए, यहाँ:

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

यह सुचारू रूप से काम करता है के रूप में वहाँ केवल एक लैम्ब्डा अभिव्यक्ति और इसलिए, एक उम्मीदवार विधि।

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

यह काम करता है, तथापि: कि विधि का नाम संकलक विशिष्ट है और कुछ सीरियल नंबर या हैश कोड, हो सकती है आदि

kludge पर लैम्ब्डा अभिव्यक्ति serializable बनाने के लिए और अपने धारावाहिक रूप निरीक्षण करने के लिए है , serializable lambdas एक उच्च कीमत पर आते हैं।


सरल समाधान जब तरीकों से अधिक पुनरावृत्ति पाया जा सकता है लैम्ब्डा अभिव्यक्ति का एक पैरामीटर के लिए टिप्पणी जोड़ने के लिए, हालांकि, वर्तमान में, javac एनोटेशन ठीक से की दुकान नहीं है, यह भी देखना this question इस बारे में हो सकता है विषय।


लेकिन क्या आप भी विचार कर सकते हैं बस कोड के बजाय एक लैम्ब्डा अभिव्यक्ति पकड़े साधारण static तरीकों का निर्माण।एक विधि के लिए Method ऑब्जेक्ट प्राप्त करना सीधे आगे है और आप अभी भी विधि संदर्भों का उपयोग करके उनमें से एक कार्यात्मक इंटरफ़ेस उदाहरण बना सकते हैं ...

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