2015-08-25 11 views
11

मैं एकाधिक तर्क विधियों को आमंत्रित करने के लिए एक तरीका ढूंढ रहा हूं लेकिन lambda निर्माण का उपयोग कर रहा हूं। प्रलेखन में यह कहा जाता है कि lambda केवल प्रयोग योग्य है यदि यह एक कार्यात्मक इंटरफ़ेस पर मैप कर सकता है।जावा 8: परिवर्तनीय तर्क के साथ लैम्ब्डा

मैं की तरह कुछ करना चाहता हूँ:

test((arg0, arg1) -> me.call(arg0, arg1)); 
test((arg0, arg1, arg2) -> me.call(arg0, arg1, arg2)); 
... 

वहाँ किसी भी तरह से एक-एक तर्क गिनती के लिए 10 इंटरफेस, एक को परिभाषित करने के बिना इस सुंदर ढंग से कर सकते हैं?

अद्यतन

मैं एक गैर विधि इंटरफ़ेस से विस्तार कई इंटरफेस का उपयोग करें और मैं विधि ओवरलोड। दो तर्क के लिए

उदाहरण: एक भी समाधान के साथ

interface Invoker {} 
interface Invoker2 extends Invoker { void invoke(Object arg0, Object arg1);} 
void test(Invoker2 invoker, Object ... arguments) { 
    test((Invoker)invoker, Object ... arguments); 
} 

void test(Invoker invoker, Object ... arguments) { 
    //Use Reflection or whatever to access the provided invoker 
} 

मैं एक संभावना के लिए आशा है कि 10 invoker इंटरफेस को बदलने के लिए और 10 अतिभारित तरीकों।

मेरे पास उचित उपयोग केस है और कृपया प्रश्न पूछें जैसे 'आप ऐसा क्यों करेंगे?' और 'आप जिस समस्या को हल करने की कोशिश कर रहे हैं वह क्या है?' या ऐसा कुछ भी। बस पता है कि मैंने इसे सोचा है और यह एक वैध समस्या है जिसे मैं हल करने की कोशिश कर रहा हूं।

इसे आक्रमणकर्ता कहने में भ्रम जोड़ने के लिए खेद है, लेकिन वास्तव में यह मेरे वर्तमान उपयोग मामले (परीक्षण निर्माता अनुबंध) में कहा जाता है।

मूल रूप से, जैसा ऊपर बताया गया है, lambda के भीतर एक अलग संख्या के गुणों के साथ काम करने वाली विधि के बारे में सोचें।

+6

संक्षिप्त उत्तर: नहीं। लंबा जवाब: nooooooooo। (लेकिन फिर, आप इस तरह की किसी चीज़ के साथ क्या करेंगे?) –

+0

क्या आपके सभी तर्क एक ही प्रकार के हैं? यदि ऐसा है तो आपने varargs का उपयोग करने में देखा है? आप जो करने की कोशिश कर रहे हैं उसके संदर्भ में एक ठोस उदाहरण क्या है? – user4987274

+0

लुई क्षमा करें, मैं आपके लिए एक अद्यतन (अद्यतन 2) बना देता हूं। –

उत्तर

1

अंतिम समाधान जो मैं वर्तमान में उपयोग करता हूं वह अंतरफलक के पदानुक्रम को परिभाषित करता है (जैसा कि प्रश्न में बताया गया है) और असफलता से बचने के लिए डिफ़ॉल्ट विधियों का उपयोग करें। छद्म कोड इस तरह दिखता है:

interface VarArgsRunnable { 
    default void run(Object ... arguments) { 
      throw new UnsupportedOperationException("not possible"); 
    } 
    default int getNumberOfArguments() { 
      throw new UnsupportedOperationException("unknown"); 
    } 
} 

और उदाहरण के लिए चार तर्क के लिए एक इंटरफ़ेस:

@FunctionalInterface 
interface VarArgsRunnable4 extends VarArgsRunnable { 
    @Override 
    default void run(Object ... arguments) { 
      assert(arguments.length == 4); 
      run(arguments[0], arguments[1], arguments[2], arguments[3]); 
    } 

    void run(Object arg0, Object arg1, Object arg2, Object arg3, Object arg4); 

    @Override 
    default int getNumberOfArguments() { 
      return 4; 
    } 
} 

VarArgsRunnable10 को VarArgsRunnable0 से 11 इंटरफेस परिभाषित एक विधि ओवरलोडिंग काफी आसान हो जाता है के बाद।

public void myMethod(VarArgsRunnable runnable, Object ... arguments) { 
    runnable.run(arguments); 
} 

के बाद से जावा instance.myMethod((index, value) -> doSomething(to(index), to(value)), 10, "value") एक जरूरत की तरह कुछ का उपयोग कर विधि सही इंटरफ़ेस का उपयोग कर ओवरलोड द्वारा VarArgsRunnable का सही विस्तारित कार्यात्मक इंटरफ़ेस का पता लगाकर एक लैम्ब्डा रचना नहीं कर सकते।

public void myMethod(VarArgsRunnable2 runnable, Object arg0, Object arg1) { 
    myMethod((VarArgsRunnable)runnable, combine(arg0, arg1)); 
} 

private static Object [] combine(Object ... values) { 
    return values; 
} 

इस के बाद से किसी भी विनियोजित प्रकार to(...) एक का उपयोग कर इस प्रयोग से बचने के लिए जेनेरिक्स का उपयोग कर parameterization लिए जा सकते हैं करने के लिए वस्तु कास्ट करने के लिए की आवश्यकता है।

to -method इस तरह दिखता है: सार्वजनिक स्थिर टी (ऑब्जेक्ट वैल्यू) { वापसी (टी) मूल्य; // दबाने इस चेतावनी }

उदाहरण लंगड़ा है, लेकिन मैं इसका इस्तेमाल जैसे कई तर्क सभी संभावित संयोजनों (परीक्षण प्रयोजनों के लिए) का क्रमपरिवर्तन होने के साथ एक विधि कॉल करने के:

run((index, value) -> doTheTestSequence(index, value), values(10, 11, 12), values("A", "B", "C")); 

तो इस छोटे से लाइन 6 invocations चलाता है। तो आप देखते हैं कि यह एक साफ सहायक है जो बहुत अधिक परिभाषित करने या टेस्टएनजी में कई तरीकों का उपयोग करने के बजाय एक ही पंक्ति में एकाधिक सामानों का परीक्षण करने में सक्षम है और जो भी ...।

पीएस: प्रतिबिंबों का उपयोग करने की कोई आवश्यकता नहीं है, यह काफी अच्छी बात है, क्योंकि यह असफल नहीं हो सकती है और तर्क गणना के अनुसार काफी बचा है।

+0

यह एक व्यावहारिक समाधान है, फिर भी एक ही समय में यह बात करता है कि जावा इस तरह की चीज करने के लिए कैसे मोड़ सकता है। मुझे आश्चर्य है कि क्या एक शानदार तरीका है। – matanster

9

Java में आपको इस तरह की एक सरणी का उपयोग करने की आवश्यकता है।

test((Object[] args) -> me.call(args)); 

call एक सरणी चर args लेता है अगर यह काम करेगा। यदि नहीं, तो आप इसके बजाय कॉल करने के लिए प्रतिबिंब का उपयोग कर सकते हैं।

+0

मैंने आपको अपना वर्तमान स्थिति देने के लिए एक अद्यतन जोड़ा। ऑब्जेक्ट [] तर्क बिल्कुल दीवार थी जिसमें मैंने भाग लिया और मैंने चारों ओर काम किया। अब मैं वर्कअराउंड को उतना ही कम करना चाहता हूं जितना कि जावा 8 की अनुमति देता है। –

+1

@ मार्टिनकेर्स्टन जावा में एक स्थिर कंपाइलर है, इसका मतलब है कि यह केवल संकलन समय पर ज्ञात विधि कॉल के लिए संकलित कर सकता है। यदि आप नहीं जानते कि किस प्रकार रनटाइम तक है, तो आपको प्रतिबिंब का उपयोग करना होगा। –

+0

मैं लैम्बडास के साथ परिवर्तनीय तर्क चाहता हूं। मुझे पता है कि वास्तव में क्या प्रतिबिंब है लेकिन आपके उदाहरण में आपको प्रत्येक कॉल के लिए एक नया ऑब्जेक्ट [] {arg0, arg1} प्रदान करना है जो वर्बोज़ है और मैं इससे बचना चाहता हूं। तो {arg0, arg1} के साथ अपना समाधान दें जो असली सौदा है जिसे मैं ढूंढ रहा हूं। प्रतिबिंब इस के साथ मदद नहीं करेगा लेकिन समाधान का हिस्सा है :-)। –

0

मेरा मानना ​​है कि आप क्या चाहते हैं निम्नलिखित कोड के लिए अनुकूल होना चाहिए:

public class Main { 
    interface Invoker { 
     void invoke(Object ... args); 
    } 

    public static void main(String[] strs) { 
     Invoker printer = new Invoker() { 
      public void invoke(Object ... args){ 
       for (Object arg: args) { 
        System.out.println(arg); 
       } 
      } 
     }; 

     printer.invoke("I", "am", "printing"); 
     invokeInvoker(printer, "Also", "printing"); 
     applyWithStillAndPrinting(printer); 
     applyWithStillAndPrinting((Object ... args) -> System.out.println("Not done")); 
     applyWithStillAndPrinting(printer::invoke); 
    } 

    public static void invokeInvoker(Invoker invoker, Object ... args) { 
     invoker.invoke(args); 
    } 

    public static void applyWithStillAndPrinting(Invoker invoker) { 
     invoker.invoke("Still", "Printing"); 
    } 
} 

ध्यान दें कि आप बना सकते हैं और एक लैम्ब्डा में पारित me.call करने के लिए है क्योंकि आप पहले से ही उस के लिए एक संदर्भ है की जरूरत नहीं है तरीका। आप test(me::call) पर कॉल कर सकते हैं जैसे कि मैं applyWithStillAndPrinting(printer::invoke) पर कॉल करता हूं।

+0

कोड के लिए धन्यवाद लेकिन मैं lambdas का उपयोग करने के लिए देख रहा था। आवेदक उदाहरण सिर्फ यही है जिसे मैंने इसे बुलाया क्योंकि यह टेस्ट परिदृश्य उत्पन्न करने वाले परीक्षण विधियों का आह्वान करने के बारे में है। –

+0

ध्यान दें कि लैम्ब्डा केवल कॉल-साइट निर्माण हैं, http://stackoverflow.com/questions/13604703/how-do-i-define-a-method-which-takes-a-lambda-as-a-parameter देखें -इन-जावा-8। उपरोक्त आक्रमणकर्ता * एक भेड़ का बच्चा है। प्रिंटर बस एक सुविधा के रूप में मौजूद है जब तक कि आपके पास कोई सवाल नहीं है कि लैम्ब्डा को कैसे परिभाषित किया जाए। – user4987274

+0

चालक लैम्ब्डा नहीं है। एक लैम्ब्डा कुछ है (arg0, arg1) -> doSomething() ;. एक लैम्ब्डा का अनुवाद एक कार्यात्मक इंटरफ़ेस में किया जाता है (मैप किया गया) - आमतौर पर @ फंक्शनल इंटरफेस के साथ एनोटेटेड माना जाता है लेकिन यह वैकल्पिक हो गया है, इसलिए यदि संभव हो तो किसी भी इंटरफ़ेस का उपयोग किया जा सकता है। तो अपने आवेदक को किसी विधि के लिए तर्क के रूप में आज़माएं और उपयोग करें (arg0, arg1) -> System.println ("message" + arg0 + "," arg1); वही करें (arg0, arg1, arg2) -> System.println ("message" + arg0 + "," + arg1 + "," + arg2)। –

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