जावा में फ़ंक्शन पॉइंटर्स


148

यह कुछ सामान्य और तुच्छ हो सकता है, लेकिन मुझे इसका ठोस जवाब खोजने में परेशानी हो रही है। C # में प्रतिनिधियों की एक अवधारणा है, जो C ++ से फ़ंक्शन पॉइंटर्स के विचार से दृढ़ता से संबंधित है। क्या जावा में समान कार्यक्षमता है? यह देखते हुए कि संकेत कुछ हद तक अनुपस्थित हैं, इस बारे में सबसे अच्छा तरीका क्या है? और स्पष्ट होने के लिए, हम यहाँ प्रथम श्रेणी की बात कर रहे हैं।


1
मैं सिर्फ इस बात के लिए उत्सुक हूं कि आप ऐसा क्यों चाहेंगे जहां श्रोता या अन्य ओओपी निर्माण अपनी वस्तु प्रकृति को बनाए रखते हुए एक ही कार्य करेंगे। मेरा मतलब है कि मैं अवधारणा द्वारा की गई कार्यक्षमता की आवश्यकता को सिर्फ इतना समझता हूं कि इसे सादे वस्तु के साथ प्राप्त किया जा सकता है .. जब तक, निश्चित रूप से मुझे कुछ याद नहीं है, इसलिए मेरा यह प्रश्न पूछना! :-)
न्यूटोपियन

2
Java 8 में लैम्बडा एक्सप्रेशन हैं: docs.oracle.com/javase/tutorial/java/javaOO/… आप इसे देखना चाहते हैं। काफी फ़ंक्शन पॉइंटर नहीं है, लेकिन अभी भी अधिक उपयोग हो सकता है।
FrustratedWithFormsDesigner 19

6
जावा 8 विधि संदर्भ ठीक वही है जो आप पूछ रहे हैं।
स्टीवन

8
जावा 8 विधि संदर्भ ठीक वही है जो आप पूछ रहे हैं। this::myMethodशब्दार्थ लैम्बडा बनाने के समान है paramA, paramB -> this.myMethod(paramA, paramB)
स्टीवन

जवाबों:


126

फ़ंक्शन-पॉइंटर-जैसी कार्यक्षमता के लिए जावा मुहावरा एक अनाम वर्ग है जो एक इंटरफ़ेस को लागू करता है, जैसे

Collections.sort(list, new Comparator<MyClass>(){
    public int compare(MyClass a, MyClass b)
    {
        // compare objects
    }
});

अद्यतन: जावा 8 से पहले जावा संस्करणों में उपरोक्त आवश्यक है। अब हमारे पास बहुत अच्छे विकल्प हैं, अर्थात् लंब्बास:

list.sort((a, b) -> a.isGreaterThan(b));

और विधि संदर्भ:

list.sort(MyClass::isGreaterThan);

2
रणनीति डिजाइन पैटर्न। जब आप फ़ंक्शन को एक गैर-वैश्विक डेटा का उपयोग करना चाहते हैं जो फ़ंक्शन के तर्क के रूप में प्रदान नहीं किया गया है, तो आप C या C ++ फ़ंक्शन पॉइंटर के रूप में बदसूरत नहीं हैं।
राएडवल्ड

75
@Raedwald C ++ में फंक्शनलर्स हैं, और C ++ 0x में क्लोजर और लैम्बडास हैं जो फंक्शनलर्स के ऊपर बने हैं। यह भी है std::bind, जो कार्यों के मापदंडों को बांधता है और एक कॉल करने योग्य वस्तु देता है। मैं इन आधारों पर सी का बचाव नहीं कर सकता, लेकिन सी ++ वास्तव में इसके लिए जावा से बेहतर है।
zneak

21
@zneak, @Raedwald: आप उपयोगकर्ता डेटा के लिए एक सूचक के साथ पास करके सी में ऐसा कर सकते हैं (जैसे: संरचना)। (और कॉलबैक अप्रत्यक्ष के प्रत्येक नए स्तर के साथ इसे अलग कर सकता है) यह वास्तव में काफी साफ है।
ब्राइस

25
हाँ, मैं वास्तव में किसी भी दिन क्रॉफ़टी रैपर कक्षाओं में सी फ़ंक्शन पॉइंटर्स लूंगा
बीटी

3
इसके लिए जावा 8 का एक बेहतर सिंटैक्स है।
थोरबजोरन राव एंडरसन

65

आप एक इंटरफ़ेस के साथ एक फ़ंक्शन पॉइंटर को प्रतिस्थापित कर सकते हैं। कहते हैं कि आप एक संग्रह के माध्यम से चलना चाहते हैं और प्रत्येक तत्व के साथ कुछ करना चाहते हैं।

public interface IFunction {
  public void execute(Object o);
}

यह वह इंटरफ़ेस है जिसे हम कुछ कह सकते हैं CollectionUtils2.doFunc (Collection c, IFunction f)।

public static void doFunc(Collection c, IFunction f) {
   for (Object o : c) {
      f.execute(o);
   }
}

एक उदाहरण के रूप में, हमारे पास संख्याओं का एक संग्रह है और आप प्रत्येक तत्व में 1 जोड़ना चाहते हैं।

CollectionUtils2.doFunc(List numbers, new IFunction() {
    public void execute(Object o) {
       Integer anInt = (Integer) o;
       anInt++;
    }
});

42

आप इसे करने के लिए प्रतिबिंब का उपयोग कर सकते हैं।

ऑब्जेक्ट और विधि नाम के पैरामीटर के रूप में पास करें (एक स्ट्रिंग के रूप में) और फिर विधि को लागू करें। उदाहरण के लिए:

Object methodCaller(Object theObject, String methodName) {
   return theObject.getClass().getMethod(methodName).invoke(theObject);
   // Catch the exceptions
}

और फिर इसे इस रूप में उपयोग करें:

String theDescription = methodCaller(object1, "toString");
Class theClass = methodCaller(object2, "getClass");

बेशक, सभी अपवादों की जांच करें और आवश्यक जातियों को जोड़ें।


7
"कैसे मैं सुस्त डॉगी जावा कोड लिखूं" को छोड़कर प्रतिबिंब कुछ भी सही जवाब नहीं है: D
जैकब

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

@zoquete .. मैं इस पोस्ट से गुज़र रहा था और मुझे संदेह था .. इसलिए आपके दृष्टिकोण में, अगर मैं "TheDescription" चर का उपयोग करता हूं, तो फ़ंक्शन को प्रत्येक और हर बार चर को एक्सेस करने के लिए कहा जाएगा ??
कार्तिक

20

नहीं, कार्य जावा में प्रथम श्रेणी की वस्तु नहीं हैं। आप एक हैंडलर क्लास को लागू करके एक ही काम कर सकते हैं - यह है कि स्विंगिंग आदि में कॉलबैक कैसे लागू किया जाता है।

हालांकि जावा के भविष्य के संस्करणों में क्लोजर (आधिकारिक नाम जो आप के बारे में बात कर रहे हैं) के लिए प्रस्ताव हैं - जवावर्ल्ड का एक दिलचस्प लेख है।


15

यह किंगडम ऑफ नाउन्स में स्टीव येजे के निष्पादन को ध्यान में रखता है । यह मूल रूप से बताता है कि जावा को हर क्रिया के लिए एक वस्तु की आवश्यकता होती है, और इसलिए फ़ंक्शन पॉइंटर्स जैसी "क्रिया-केवल" इकाइयां नहीं होती हैं।


8

समान कार्यक्षमता प्राप्त करने के लिए आप अनाम आंतरिक कक्षाओं का उपयोग कर सकते हैं।

यदि आप एक इंटरफ़ेस को परिभाषित करने वाले थे Foo:

interface Foo {
    Object myFunc(Object arg);
}

एक विधि बनाएँ barजो एक तर्क के रूप में 'फ़ंक्शन पॉइंटर' प्राप्त करेगी:

public void bar(Foo foo) {
    // .....
    Object object = foo.myFunc(argValue);
    // .....
}

अंत में विधि को इस प्रकार कहें:

bar(new Foo() {
    public Object myFunc(Object arg) {
        // Function code.
    }
}

7

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

जावा सामान्य कार्यात्मक इंटरफेस का एक सेट प्रदान करता है । जब आप निम्नलिखित कर सकते हैं:

public class Test {
   public void test1(Integer i) {}
   public void test2(Integer i) {}
   public void consumer(Consumer<Integer> a) {
     a.accept(10);
   }
   public void provideConsumer() {
     consumer(this::test1);   // method reference
     consumer(x -> test2(x)); // lambda
   }
}

वहाँ की अवधारणा है alias: जैसे public List<T> collect(T t) = Collections::collect
jadadba

@ जजदबा जहाँ तक मुझे पता है।
एलेक्स

5

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

सिंथेटिक रूप से, यह कुछ हद तक अनाम कक्षाओं या कक्षा के सदस्य चर के रूप में परिभाषित अनाम वर्गों में परिभाषित अनाम कक्षाओं का उपयोग करके कम किया जा सकता है।

उदाहरण:

class MyComponent extends JPanel {
    private JButton button;
    public MyComponent() {
        button = new JButton("click me");
        button.addActionListener(buttonAction);
        add(button);
    }

    private ActionListener buttonAction = new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            // handle the event...
            // note how the handler instance can access 
            // members of the surrounding class
            button.setText("you clicked me");
        }
    }
}

3

मैंने प्रतिबिंब का उपयोग करके जावा में कॉलबैक / प्रतिनिधि समर्थन को लागू किया है। विवरण और कार्य स्रोत मेरी वेबसाइट पर उपलब्ध हैं ।

यह काम किस प्रकार करता है

हमारे पास कॉलबैक नाम का एक सिद्धांत वर्ग है, जिसका नाम नस्टर्ड क्लास विथपार्म्स है। जिस API को कॉलबैक की आवश्यकता है, वह कॉलबैक ऑब्जेक्ट को एक पैरामीटर के रूप में लेगा और यदि neccessary है, तो कॉलबैक .Parms को विधि चर के रूप में बनाएँ। चूंकि इस ऑब्जेक्ट के बहुत सारे अनुप्रयोग पुनरावर्ती होंगे, यह बहुत सफाई से काम करता है।

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

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

WithParms नेस्टेड क्लास है, तब, वैकल्पिक है और दो उद्देश्यों को पूरा करता है, इसमें कॉलबैक इनवोकेशन के लिए आवश्यक पैरामीटर ऑब्जेक्ट सरणी है, और यह 10 ओवरलोडेड इनवोक () विधियों (1 से 10 मापदंडों के साथ) प्रदान करता है जो पैरामीटर सरणी को लोड करते हैं और फिर कॉलबैक लक्ष्य प्राप्त करें।



-1

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

 /*Just to merge functions in a common name*/
 public class CustomFunction{ 
 public CustomFunction(){}
 }

 /*Actual functions*/
 public class Function1 extends CustomFunction{
 public Function1(){}
 public void execute(){...something here...}
 }

 public class Function2 extends CustomFunction{
 public Function2(){}
 public void execute(){...something here...}
 }

 .....
 /*in Main class*/
 CustomFunction functionpointer = null;

फिर आवेदन के आधार पर असाइन करें

 functionpointer = new Function1();
 functionpointer = new Function2();

आदि।

और फोन करके

 functionpointer.execute();
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.