पैरामीटर के रूप में जावा पास विधि


277

मैं संदर्भ द्वारा एक विधि पारित करने के लिए एक रास्ता तलाश रहा हूं। मैं समझता हूं कि जावा मानकों को मापदंडों के रूप में पारित नहीं करता है, हालांकि, मैं एक विकल्प प्राप्त करना चाहूंगा।

मुझे बताया गया है कि इंटरफेस पैरामीटर के रूप में गुजरने के विकल्प हैं, लेकिन मुझे समझ में नहीं आता है कि एक इंटरफ़ेस संदर्भ द्वारा विधि के रूप में कैसे कार्य कर सकता है। अगर मैं सही ढंग से समझूं तो एक इंटरफ़ेस केवल उन तरीकों का एक सार सेट है जो परिभाषित नहीं हैं। मैं एक इंटरफ़ेस भेजना नहीं चाहता जिसे हर बार परिभाषित किया जाना चाहिए क्योंकि कई अलग-अलग तरीके समान पैरामीटर के साथ एक ही विधि को कॉल कर सकते हैं।

जो मैं पूरा करना चाहूंगा वह कुछ इस तरह है:

public void setAllComponents(Component[] myComponentArray, Method myMethod) {
    for (Component leaf : myComponentArray) {
        if (leaf instanceof Container) { //recursive call if Container
            Container node = (Container) leaf;
            setAllComponents(node.getComponents(), myMethod);
        } //end if node
        myMethod(leaf);
    } //end looping through components
}

जैसे आमंत्रित किया गया:

setAllComponents(this.getComponents(), changeColor());
setAllComponents(this.getComponents(), changeSize());

अभी मेरा समाधान एक अतिरिक्त पैरामीटर पास करना है और उचित विधि का चयन करने के लिए अंदर स्विच केस का उपयोग करना है। हालांकि, यह समाधान कोड के पुन: उपयोग के लिए उधार नहीं देता है।

इसी तरह के सवाल के लिए इस उत्तर को भी देखें stackoverflow.com/a/22933032/1010868
टॉमस गावेल

जवाबों:


233

संपादित करें : जावा 8 के रूप में, लैम्ब्डा अभिव्यक्ति एक अच्छा समाधान है क्योंकि अन्य उत्तरों ने बताया है। नीचे उत्तर जावा 7 और पहले के लिए लिखा गया था ...


कमांड पैटर्न पर एक नज़र डालें ।

// NOTE: code not tested, but I believe this is valid java...
public class CommandExample 
{
    public interface Command 
    {
        public void execute(Object data);
    }

    public class PrintCommand implements Command 
    {
        public void execute(Object data) 
        {
            System.out.println(data.toString());
        }    
    }

    public static void callCommand(Command command, Object data) 
    {
        command.execute(data);
    }

    public static void main(String... args) 
    {
        callCommand(new PrintCommand(), "hello world");
    }
}

संपादित करें: जैसा कि पीट किर्कम बताते हैं , विज़िटर का उपयोग करके ऐसा करने का एक और तरीका है । आगंतुक दृष्टिकोण थोड़ा अधिक शामिल है - आपके नोड्स को एक acceptVisitor()विधि के साथ आगंतुक-जागरूक होने की आवश्यकता है - लेकिन अगर आपको अधिक जटिल वस्तु ग्राफ को पार करने की आवश्यकता है, तो यह जांचने योग्य है।


2
@ मैक - अच्छा! यह प्रथम श्रेणी के तरीकों के बिना भाषाओं में बार-बार आता है और उन्हें अनुकरण करने का वास्तविक तरीका है, इसलिए यह याद रखने योग्य है।
दान विंटन

7
यह विज़िटर पैटर्न है (संग्रह के प्रत्येक सदस्य पर लागू फ़ंक्शन से एक संग्रह पर पुनरावृत्ति की कार्रवाई को अलग करें), कमांड पैटर्न नहीं (किसी ऑब्जेक्ट में विधि कॉल के लिए तर्कों को एन्क्रिप्ट करें)। आप विशेष रूप से तर्क को अतिक्रमण नहीं कर रहे हैं - यह आगंतुक पैटर्न के पुनरावृत्ति भाग द्वारा प्रदान किया जाता है।
पीट किर्कम

नहीं, आपको केवल स्वीकार पद्धति की आवश्यकता है यदि आप दोहरे प्रेषण के साथ संयोजन कर रहे हैं। यदि आपके पास एक मोनोमोर्फिक आगंतुक है, तो यह आपके द्वारा ऊपर दिए गए कोड के समान है।
पीट किर्कम

जावा 8 में ex.operS (स्ट्रिंग :: toLowerCase, "STRING") की तरह हो सकता है। अच्छा लेख देखें: studytrails.com/java/java8/…
Zon

पीट किर्कम सही है: आपका कोड विज़िटर पैटर्न लागू कर रहा है, कमांड पैटर्न नहीं (और यह अच्छा है, क्योंकि यह वही है जो ओपी को चाहिए।) जैसा कि पीट कहते हैं, आप तर्क को अतिक्रमण नहीं कर रहे हैं, इसलिए आप कमांड नहीं कर रहे हैं - आपका आदेश इंटरफ़ेस में एक निष्पादन होता है जो एक पैरामीटर लेता है। विकिपीडिया नहीं करता है। यह कमांड पैटर्न के इरादे के लिए मौलिक है। जैसा कि पहले पैराग्राफ में कहा गया है, " सभी सूचनाओं को इनकैप्सुलेट करें ... इस जानकारी में विधि का नाम, वह वस्तु जो विधि मापदंडों के लिए विधि और मूल्यों का मालिक है ।"
टूलमेकरसैट

73

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

लंबोदर भावों के बिना:

obj.aMethod(new AFunctionalInterface() {
    @Override
    public boolean anotherMethod(int i)
    {
        return i == 982
    }
});

लंबोदर भाव के साथ:

obj.aMethod(i -> i == 982);

यहाँ लैम्ब्डा एक्सप्रेशंस पर जावा ट्यूटोरियल का एक अंश दिया गया है :

लैम्ब्डा एक्सप्रेशंस का सिंटेक्स

एक लंबोदर अभिव्यक्ति में निम्नलिखित शामिल हैं:

  • कोष्ठकों में संलग्न औपचारिक मापदंडों की अल्पविराम से अलग सूची। CheckPerson.test पद्धति में एक पैरामीटर, p है, जो व्यक्ति वर्ग का एक उदाहरण प्रस्तुत करता है।

    नोट : आप एक लैम्ब्डा अभिव्यक्ति में डेटा प्रकार के मापदंडों को छोड़ सकते हैं। इसके अलावा, यदि आप केवल एक पैरामीटर हैं, तो आप कोष्ठकों को छोड़ सकते हैं। उदाहरण के लिए, निम्नलिखित लैम्ब्डा अभिव्यक्ति भी मान्य है:

    p -> p.getGender() == Person.Sex.MALE 
        && p.getAge() >= 18
        && p.getAge() <= 25
  • तीर टोकन, ->

  • एक निकाय, जिसमें एक एकल अभिव्यक्ति या एक स्टेटमेंट ब्लॉक होता है। यह उदाहरण निम्नलिखित अभिव्यक्ति का उपयोग करता है:

    p.getGender() == Person.Sex.MALE 
        && p.getAge() >= 18
        && p.getAge() <= 25

    यदि आप एक एकल अभिव्यक्ति निर्दिष्ट करते हैं, तो जावा रनटाइम अभिव्यक्ति का मूल्यांकन करता है और फिर उसका मूल्य लौटाता है। वैकल्पिक रूप से, आप रिटर्न स्टेटमेंट का उपयोग कर सकते हैं:

    p -> {
        return p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25;
    }

    एक वापसी बयान एक अभिव्यक्ति नहीं है; एक लंबोदर अभिव्यक्ति में, आपको ब्रेसिज़ ({}) में कथनों को जोड़ना होगा। हालांकि, आपको ब्रेसिज़ में एक शून्य विधि आह्वान संलग्न करने की आवश्यकता नहीं है। उदाहरण के लिए, निम्नलिखित एक मान्य लंबोदर अभिव्यक्ति है:

    email -> System.out.println(email)

ध्यान दें कि एक लैम्ब्डा अभिव्यक्ति एक विधि घोषणा की तरह दिखता है; आप नाम के बिना लंबोदर के भावों को गुमनाम तरीकों पर विचार कर सकते हैं।


यहां बताया गया है कि कैसे आप एक लंबोदर अभिव्यक्ति का उपयोग करके "एक विधि पास कर सकते हैं":

interface I {
    public void myMethod(Component component);
}

class A {
    public void changeColor(Component component) {
        // code here
    }

    public void changeSize(Component component) {
        // code here
    }
}
class B {
    public void setAllComponents(Component[] myComponentArray, I myMethodsInterface) {
        for(Component leaf : myComponentArray) {
            if(leaf instanceof Container) { // recursive call if Container
                Container node = (Container)leaf;
                setAllComponents(node.getComponents(), myMethodInterface);
            } // end if node
            myMethodsInterface.myMethod(leaf);
        } // end looping through components
    }
}
class C {
    A a = new A();
    B b = new B();

    public C() {
        b.setAllComponents(this.getComponents(), component -> a.changeColor(component));
        b.setAllComponents(this.getComponents(), component -> a.changeSize(component));
    }
}

Cविधि संदर्भों के उपयोग से कक्षा को थोड़ा और आगे बढ़ाया जा सकता है जैसे:

class C {
    A a = new A();
    B b = new B();

    public C() {
        b.setAllComponents(this.getComponents(), a::changeColor);
        b.setAllComponents(this.getComponents(), a::changeSize);
    }
}

क्या क्लास ए को इनहेरिटेड फॉर्म इंटरफ़ेस की आवश्यकता है?
Serob_b

1
@Serob_b नहींं। जब तक आप इसे एक विधि संदर्भ ( ::ऑपरेटर देखें ) के रूप में पारित करना चाहते हैं, तब तक कोई फर्क नहीं पड़ता कि ए क्या है। a.changeThing(component)किसी भी बयान या कोड ब्लॉक को आप चाहें तब तक बदल सकते हैं, जब तक यह शून्य न हो जाए।
गाय द हैट

29

java.lang.reflect.Methodऑब्जेक्ट का उपयोग करें और कॉल करेंinvoke


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

3
क्या आपने जावास्क्रिप्ट fg में सुरक्षा टाइप की है? प्रकार सुरक्षा एक तर्क नहीं है।
डेन्यूबियन नाविक

13
टाइप सेफ्टी कैसे एक तर्क नहीं है, जब प्रश्न में भाषा अपने सबसे मजबूत घटकों में से एक के रूप में सुरक्षा रखती है? जावा एक दृढ़ता से टाइप की जाने वाली भाषा है, और यह मजबूत टाइपिंग उन कारणों में से एक है, जिन्हें आप इसे किसी अन्य संकलित भाषा पर ले जाएंगे।
एडम पार्किंन

21
"मूल प्रतिबिंब सुविधा मूल रूप से घटक-आधारित एप्लिकेशन बिल्डर टूल के लिए डिज़ाइन की गई थी। [...] एक नियम के रूप में, वस्तुओं को रनटाइम पर सामान्य अनुप्रयोगों में प्रतिबिंबित नहीं किया जाना चाहिए।" आइटम 53: प्रभावी जावा द्वितीय संस्करण से प्रतिबिंब को प्राथमिकता दें। - यही कारण है कि जावा के ;-) रचनाकारों के बारे में सोच की लाइन है
विलहेम Meignan

8
प्रतिबिंबित करने का औचित्यपूर्ण उपयोग नहीं। मैं सभी अपवित्रों को देखकर भयभीत हूं। प्रतिबिंबित कभी भी एक सामान्य प्रोग्रामिंग तंत्र के रूप में इस्तेमाल करने का इरादा नहीं था; केवल इसका उपयोग करें जब कोई अन्य साफ समाधान न हो।
टूलमेकर 14

22

जावा 8 के बाद से एक Function<T, R>इंटरफ़ेस ( डॉक्स ) है, जिसमें विधि है

R apply(T t);

आप इसका उपयोग अन्य कार्यों के मापदंडों के रूप में कार्य करने के लिए कर सकते हैं। T फ़ंक्शन का इनपुट प्रकार है, R रिटर्न प्रकार है।

अपने उदाहरण में आपको एक फ़ंक्शन पास करना होगा Componentजो इनपुट के रूप में टाइप करता है और कुछ भी नहीं लौटाता है - Void। इस मामले Function<T, R>में सबसे अच्छा विकल्प नहीं है, क्योंकि शून्य प्रकार का कोई ऑटोबॉक्सिंग नहीं है। जिस इंटरफ़ेस की आप तलाश कर रहे हैं उसे विधि के साथ Consumer<T>( डॉक्स ) कहा जाता है

void accept(T t);

यह इस तरह दिखेगा:

public void setAllComponents(Component[] myComponentArray, Consumer<Component> myMethod) {
    for (Component leaf : myComponentArray) {
        if (leaf instanceof Container) { 
            Container node = (Container) leaf;
            setAllComponents(node.getComponents(), myMethod);
        } 
        myMethod.accept(leaf);
    } 
}

और आप इसे विधि संदर्भों का उपयोग करके कहेंगे:

setAllComponents(this.getComponents(), this::changeColor);
setAllComponents(this.getComponents(), this::changeSize); 

यह मानते हुए कि आपने एक ही वर्ग में चेंजकोलर () और चेंजाइज () तरीके परिभाषित किए हैं।


यदि आपकी विधि एक से अधिक पैरामीटर स्वीकार करने के लिए होती है, तो आप उपयोग कर सकते हैं BiFunction<T, U, R> - टी और यू प्रकार के इनपुट पैरामीटर और आर रिटर्न प्रकार। वहाँ भी है BiConsumer<T, U>(दो तर्क, कोई वापसी प्रकार)। दुर्भाग्य से 3 और अधिक इनपुट मापदंडों के लिए, आपको अपने आप से एक इंटरफ़ेस बनाना होगा। उदाहरण के लिए:

public interface Function4<A, B, C, D, R> {

    R apply(A a, B b, C c, D d);
}

19

पहले एक इंटरफ़ेस को परिभाषित करें जिसमें आप एक पैरामीटर के रूप में पास करना चाहते हैं

public interface Callable {
  public void call(int param);
}

विधि के साथ एक वर्ग को लागू करें

class Test implements Callable {
  public void call(int param) {
    System.out.println( param );
  }
}

// इस तरह आमंत्रित करें

Callable cmd = new Test();

यह आपको cmd को पैरामीटर के रूप में पारित करने और इंटरफ़ेस में परिभाषित विधि कॉल को लागू करने की अनुमति देता है

public invoke( Callable callable ) {
  callable.call( 5 );
}

1
आपको अपना स्वयं का इंटरफ़ेस नहीं बनाना पड़ सकता है क्योंकि जावा ने आपके लिए उनमें से बहुत कुछ परिभाषित किया है: docs.oracle.com/javase/8/docs/api/java/util/function/…
पतला '

@ स्लीम दिलचस्प बिंदु, वे परिभाषाएँ कितनी स्थिर हैं, क्या वे आपके द्वारा सुझाए गए रूप में उपयोग किए जाने वाले हैं या टूटने की संभावना है?
मैनुअल

@slim वास्तव में, डॉक्स का जवाब है कि: "इस पैकेज में इंटरफेस JDK द्वारा उपयोग किए जाने वाले सामान्य प्रयोजन कार्यात्मक इंटरफेस हैं, और उपयोगकर्ता कोड द्वारा भी उपयोग किए जाने के लिए उपलब्ध हैं।"
मैनुअल

14

हालांकि यह अभी तक जावा 7 और नीचे के लिए मान्य नहीं है, मेरा मानना ​​है कि हमें भविष्य को देखना चाहिए और कम से कम जावा 8 जैसे नए संस्करणों में आने वाले परिवर्तनों को पहचानना चाहिए ।

अर्थात्, यह नया संस्करण लैम्ब्डा और विधि संदर्भ जावा के साथ लाता है नए एपीआई , जो इस समस्या के लिए एक और वैध है। जबकि उन्हें अभी भी एक इंटरफ़ेस की आवश्यकता है कोई नई वस्तु नहीं बनाई गई है, और अतिरिक्त क्लासफाइल्स को अलग-अलग होने के कारण आउटपुट निर्देशिकाओं की आवश्यकता नहीं है। जेवीएम द्वारा हैंडलिंग।

दोनों स्वादों (लैम्ब्डा और विधि संदर्भ) को एक एकल विधि के साथ उपलब्ध इंटरफेस की आवश्यकता होती है जिसका हस्ताक्षर उपयोग किया जाता है:

public interface NewVersionTest{
    String returnAString(Object oIn, String str);
}

तरीकों के नाम यहां से मायने नहीं रखेंगे। जहां एक लंबोदर स्वीकार किया जाता है, एक विधि संदर्भ भी है। उदाहरण के लिए, यहां हमारे हस्ताक्षर का उपयोग करने के लिए:

public static void printOutput(NewVersionTest t, Object o, String s){
    System.out.println(t.returnAString(o, s));
}

यह सिर्फ एक सरल इंटरफ़ेस आह्वान है, जब तक लैम्ब्डा 1 पास नहीं हो जाता:

public static void main(String[] args){
    printOutput( (Object oIn, String sIn) -> {
        System.out.println("Lambda reached!");
        return "lambda return";
    }
    );
}

यह आउटपुट होगा:

Lambda reached!
lambda return

विधि संदर्भ समान हैं। दिया हुआ:

public class HelperClass{
    public static String testOtherSig(Object o, String s){
        return "real static method";
    }
}

और मुख्य:

public static void main(String[] args){
    printOutput(HelperClass::testOtherSig);
}

आउटपुट होगा real static methodविधि संदर्भ स्थिर, उदाहरण, गैर-स्थैतिक के साथ मनमाना उदाहरण, और यहां तक ​​कि कंस्ट्रक्टर भी हो सकते हैं । कंस्ट्रक्टर के लिए कुछ का ClassName::newउपयोग किया जाना चाहिए।

1 इसे कुछ लोगों द्वारा लंबोदर नहीं माना जाता है, क्योंकि इसके दुष्प्रभाव हैं। यह उदाहरण के लिए, एक और अधिक सरल-से-स्पष्ट फैशन में एक का उपयोग करता है।


12

पिछली बार जब मैंने जाँच की थी, तो जावा मूल रूप से वह करने में सक्षम नहीं है जो आप चाहते हैं; ऐसी सीमाओं के आसपास जाने के लिए आपको 'वर्क-अराउंड' का उपयोग करना होगा। जहां तक ​​मैं इसे देखता हूं, इंटरफेस एक विकल्प है, लेकिन एक अच्छा विकल्प नहीं है। शायद जिसने भी आपसे कहा कि वह कुछ इस तरह था:

public interface ComponentMethod {
  public abstract void PerfromMethod(Container c);
}

public class ChangeColor implements ComponentMethod {
  @Override
  public void PerfromMethod(Container c) {
    // do color change stuff
  }
}

public class ChangeSize implements ComponentMethod {
  @Override
  public void PerfromMethod(Container c) {
    // do color change stuff
  }
}

public void setAllComponents(Component[] myComponentArray, ComponentMethod myMethod) {
    for (Component leaf : myComponentArray) {
        if (leaf instanceof Container) { //recursive call if Container
            Container node = (Container) leaf;
            setAllComponents(node.getComponents(), myMethod);
        } //end if node
        myMethod.PerfromMethod(leaf);
    } //end looping through components
}

फिर आप किसके साथ आमंत्रित करेंगे:

setAllComponents(this.getComponents(), new ChangeColor());
setAllComponents(this.getComponents(), new ChangeSize());

6

यदि आपको कुछ वापस करने के लिए इन विधियों की आवश्यकता नहीं है, तो आप उन्हें रन करने योग्य ऑब्जेक्ट वापस कर सकते हैं।

private Runnable methodName (final int arg) {
    return (new Runnable() {
        public void run() {
          // do stuff with arg
        }
    });
}

फिर इसका उपयोग करें जैसे:

private void otherMethodName (Runnable arg){
    arg.run();
}

2

मुझे कोई उदाहरण नहीं मिला जो मेरे लिए पर्याप्त हो कि मैं java.util.function.Functionसाधारण फ़ंक्शन के पैरामीटर पैरामीटर के रूप में कैसे उपयोग करूं । ये रहा एक सरल उदाहरण:

import java.util.function.Function;

public class Foo {

  private Foo(String parameter) {
    System.out.println("I'm a Foo " + parameter);
  }

  public static Foo method(final String parameter) {
    return new Foo(parameter);
  }

  private static Function parametrisedMethod(Function<String, Foo> function) {
    return function;
  }

  public static void main(String[] args) {
    parametrisedMethod(Foo::method).apply("from a method");
  }
}

मूल रूप से आपके पास Fooडिफ़ॉल्ट निर्माता के साथ एक ऑब्जेक्ट है। एक methodजिसे एक पैरामीटर के रूप में कहा जाएगा parametrisedMethodजिसमें से प्रकार है Function<String, Foo>

  • Function<String, Foo>इसका मतलब है कि फ़ंक्शन एक Stringपैरामीटर के रूप में लेता है और वापस लौटता है Foo
  • Foo::Methodकी तरह एक लैम्ब्डा के अनुरूपx -> Foo.method(x);
  • parametrisedMethod(Foo::method) के रूप में देखा जा सकता है x -> parametrisedMethod(Foo.method(x))
  • .apply("from a method")ऐसा करने के लिए मूल रूप से हैparametrisedMethod(Foo.method("from a method"))

फिर आउटपुट में वापस आएगा:

>> I'm a Foo from a method

उदाहरण के रूप में चल रहा है, तो आप विभिन्न वर्गों और इंटरफेस के साथ उपरोक्त जवाब से अधिक जटिल सामान की कोशिश कर सकते हैं।


एंड्रॉइड में अप्लाई कॉल का उपयोग करने के लिए आपको एक न्यूनतम
एपि

1

जावा में नाम पास करने और इसे कॉल करने के लिए एक तंत्र है। यह परावर्तन तंत्र का हिस्सा है। आपके फ़ंक्शन को क्लास मेथड का अतिरिक्त पैरामीटर लेना चाहिए।

public void YouMethod(..... Method methodToCall, Object objWithAllMethodsToBeCalled)
{
...
Object retobj = methodToCall.invoke(objWithAllMethodsToBeCalled, arglist);
...
}

1

मैं एक जावा विशेषज्ञ नहीं हूं, लेकिन मैं आपकी समस्या का समाधान करता हूं:

@FunctionalInterface
public interface AutoCompleteCallable<T> {
  String call(T model) throws Exception;
}

मैं अपने विशेष इंटरफ़ेस में पैरामीटर को परिभाषित करता हूं

public <T> void initialize(List<T> entries, AutoCompleteCallable getSearchText) {.......
//call here
String value = getSearchText.call(item);
...
}

प्रारंभ में, मैं आरंभिक विधि को कॉल करते हुए getSearchText विधि को लागू करता हूं

initialize(getMessageContactModelList(), new AutoCompleteCallable() {
          @Override
          public String call(Object model) throws Exception {
            return "custom string" + ((xxxModel)model.getTitle());
          }
        })

वास्तव में यह सबसे अच्छा जवाब है और इसे करने का उचित तरीका है। और अधिक +1
amdev

0

प्रेक्षक पैटर्न का उपयोग करें (कभी-कभी श्रोता पैटर्न भी कहा जाता है):

interface ComponentDelegate {
    void doSomething(Component component);
}

public void setAllComponents(Component[] myComponentArray, ComponentDelegate delegate) {
    // ...
    delegate.doSomething(leaf);
}

setAllComponents(this.getComponents(), new ComponentDelegate() {
                                            void doSomething(Component component) {
                                                changeColor(component); // or do directly what you want
                                            }
                                       });

new ComponentDelegate()... इंटरफ़ेस को लागू करने वाला एक अनाम प्रकार घोषित करता है।


8
यह वह पैटर्न नहीं है जिसकी आप तलाश कर रहे हैं।
पीट किर्कम

1
प्रेक्षक पैटर्न एक परिवर्तन का जवाब देने की क्षमता को अमूर्त करने के बारे में है। ओपी प्रत्येक आइटम पर की गई कार्रवाई को संग्रह से दूर होने वाले कोड से दूर रखना चाहता है, जो आगंतुक पैटर्न है।
पीट किर्कम

प्रेक्षक / श्रोता पैटर्न वास्तव में उस कमांड पैटर्न के समान है। वे केवल इरादे में भिन्न होते हैं। पर्यवेक्षक अधिसूचना के बारे में है जबकि कमांड प्रथम श्रेणी के कार्यों / लंबोदर का विकल्प है। दूसरी ओर आगंतुक कुछ पूरी तरह से अलग है। मुझे नहीं लगता कि इसे कुछ वाक्यों में समझाया जा सकता है, इसलिए कृपया en.wikipedia.org/wiki/Visitor_pattern
EricSiplefer

0

यहाँ एक बुनियादी उदाहरण है:

public class TestMethodPassing
{
    private static void println()
    {
        System.out.println("Do println");
    }

    private static void print()
    {
        System.out.print("Do print");
    }

    private static void performTask(BasicFunctionalInterface functionalInterface)
    {
        functionalInterface.performTask();
    }

    @FunctionalInterface
    interface BasicFunctionalInterface
    {
        void performTask();
    }

    public static void main(String[] arguments)
    {
        performTask(TestMethodPassing::println);
        performTask(TestMethodPassing::print);
    }
}

आउटपुट:

Do println
Do print

0

मुझे यहां कोई समाधान नहीं मिला जो यह दर्शाता हो कि किसी विधि के पैरामीटर के रूप में इसके साथ जुड़े मापदंडों को कैसे पारित किया जाए। Bellow का उदाहरण है कि आप कैसे पहले से बाध्य पैरामीटर मानों के साथ एक विधि पारित कर सकते हैं।

  1. चरण 1: वापसी के साथ दो इंटरफेस बनाएं, एक और बिना। जावा में समान इंटरफेस है लेकिन वे कम व्यावहारिक उपयोग के हैं क्योंकि वे अपवाद फेंकने का समर्थन नहीं करते हैं।


    public interface Do {
    void run() throws Exception;
    }


    public interface Return {
        R run() throws Exception;
    }
  1. लेन-देन में विधि कॉल को रैप करने के लिए हम दोनों इंटरफेस का उपयोग कैसे करते हैं इसका उदाहरण। ध्यान दें कि हम वास्तविक मापदंडों के साथ विधि पास करते हैं।


    //example - when passed method does not return any value
    public void tx(final Do func) throws Exception {
        connectionScope.beginTransaction();
        try {
            func.run();
            connectionScope.commit();
        } catch (Exception e) {
            connectionScope.rollback();
            throw e;
        } finally {
            connectionScope.close();
        }
    }

    //Invoke code above by 
    tx(() -> api.delete(6));

एक और उदाहरण से पता चलता है कि एक ऐसी विधि कैसे पारित की जाती है जो वास्तव में कुछ लौटाती है



        public  R tx(final Return func) throws Exception {
    R r=null;
    connectionScope.beginTransaction();
    try {
                r=func.run();
                connectionScope.commit();
            } catch (Exception e) {
                connectionScope.rollback();
                throw e;
            } finally {
                connectionScope.close();
            }
        return r;
        }
        //Invoke code above by 
        Object x= tx(() -> api.get(id));

0

परावर्तन, उत्तीर्ण विधि के साथ समाधान का उदाहरण सार्वजनिक होना चाहिए

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

public class Program {
    int i;

    public static void main(String[] args) {
        Program   obj = new Program();    //some object

        try {
            Method method = obj.getClass().getMethod("target");
            repeatMethod( 5, obj, method );
        } 
        catch ( NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            System.out.println( e ); 
        }
    }

    static void repeatMethod (int times, Object object, Method method)
        throws IllegalAccessException, InvocationTargetException {

        for (int i=0; i<times; i++)
            method.invoke(object);
    }
    public void target() {                 //public is necessary
        System.out.println("target(): "+ ++i);
    }
}

0

मैं ऊपर दिए गए उत्तरों की सराहना करता हूं, लेकिन मैं नीचे दिए गए तरीके का उपयोग करके समान व्यवहार प्राप्त करने में सक्षम था; जावास्क्रिप्ट कॉलबैक से उधार लिया गया एक विचार। मैं सुधार के लिए खुला हूं, हालांकि अभी तक अच्छा (उत्पादन में) हूं।

हस्ताक्षर में फ़ंक्शन के रिटर्न प्रकार का उपयोग करने का विचार है, जिसका अर्थ है कि उपज को स्थिर करना होगा।

नीचे एक फ़ंक्शन है जो टाइमआउट के साथ एक प्रक्रिया चलाता है।

public static void timeoutFunction(String fnReturnVal) {

    Object p = null; // whatever object you need here

    String threadSleeptime = null;

    Config config;

    try {
        config = ConfigReader.getConfigProperties();
        threadSleeptime = config.getThreadSleepTime();

    } catch (Exception e) {
        log.error(e);
        log.error("");
        log.error("Defaulting thread sleep time to 105000 miliseconds.");
        log.error("");
        threadSleeptime = "100000";
    }

    ExecutorService executor = Executors.newCachedThreadPool();
    Callable<Object> task = new Callable<Object>() {
        public Object call() {
            // Do job here using --- fnReturnVal --- and return appropriate value
            return null;
        }
    };
    Future<Object> future = executor.submit(task);

    try {
        p = future.get(Integer.parseInt(threadSleeptime), TimeUnit.MILLISECONDS);
    } catch (Exception e) {
        log.error(e + ". The function timed out after [" + threadSleeptime
                + "] miliseconds before a response was received.");
    } finally {
        // if task has started then don't stop it
        future.cancel(false);
    }
}

private static String returnString() {
    return "hello";
}

public static void main(String[] args) {
    timeoutFunction(returnString());
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.