जावा में फ़ंक्शन पॉइंटर के लिए निकटतम विकल्प क्या है?


299

मेरे पास एक विधि है जो कोड की दस पंक्तियों के बारे में है। मैं अधिक विधियाँ बनाना चाहता हूँ जो बिल्कुल वही काम करें, एक छोटी गणना को छोड़कर जो कोड की एक पंक्ति को बदलने जा रही है। यह फ़ंक्शन पॉइंटर को उस एक पंक्ति को बदलने के लिए पास करने के लिए एक सही अनुप्रयोग है, लेकिन जावा में फ़ंक्शन पॉइंटर्स नहीं हैं। मेरा सबसे अच्छा विकल्प क्या है?


5
जावा 8 में लैम्ब्डा एक्सप्रेशन होंगे । आप यहाँ लैम्बडा एक्सप्रेशन के बारे में अधिक पढ़ सकते हैं ।
मेरिज

4
@ मारीमी मुझे नहीं लगता कि लैंबडा एक्सप्रेशन को फंक्शन पॉइंटर्स के रूप में गिना जाता है। ऑपरेटर, दूसरे हाथ पर ...::
टोपी के साथ लड़का

देर से टिप्पणी के लिए क्षमा करें;) - आमतौर पर, आपको उसके लिए फ़ंक्शन पॉइंटर की आवश्यकता नहीं होती है। बस एक टेम्पलेट विधि का उपयोग करें! ( en.wikipedia.org/wiki/Template_method_pattern )
isnot2bad

2
@ isnot2bad - उस लेख को देखकर लगता है कि ओवरकिल - यहाँ दिए गए उत्तरों की तुलना में अधिक जटिल है। विशेष रूप से, टेम्पलेट पद्धति में प्रत्येक वैकल्पिक गणना के लिए एक उपवर्ग बनाने की आवश्यकता होती है । मुझे ऐसा कुछ नहीं दिखाई दे रहा है जिसमें उपवर्गों की आवश्यकता हो ; वह बस कई तरीके बनाना चाहता है , और अधिकांश कार्यान्वयन साझा करता है। जैसा कि स्वीकृत जवाब से पता चलता है, यह आसानी से "जगह में" (प्रत्येक विधि के अंदर) किया जाता है, जावा 8 से पहले भी अपने लैम्ब्डा के साथ।
टूलमेकरसैट

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

जवाबों:


269

अनाम आंतरिक वर्ग

कहते हैं कि तुम एक समारोह है Stringकि एक रिटर्न के साथ पारित किया है चाहता हूँ एक int
पहले आपको अपने एकमात्र सदस्य के रूप में फ़ंक्शन के साथ एक इंटरफ़ेस को परिभाषित करना होगा, यदि आप किसी मौजूदा का पुन: उपयोग नहीं कर सकते हैं।

interface StringFunction {
    int func(String param);
}

एक विधि जो पॉइंटर लेती है वह बस StringFunctionउदाहरण को स्वीकार करेगी :

public void takingMethod(StringFunction sf) {
   int i = sf.func("my string");
   // do whatever ...
}

और इस तरह बुलाया जाएगा:

ref.takingMethod(new StringFunction() {
    public int func(String param) {
        // body
    }
});

संपादित करें: जावा 8 में, आप इसे लंबोदर अभिव्यक्ति के साथ कह सकते हैं:

ref.takingMethod(param -> bodyExpression);

13
यह "कमांड पेटरन" का एक उदाहरण है, वैसे। en.wikipedia.org/wiki/Command_Pattern
Ogre Psalm33

3
@Ogre Psalm33 यह तकनीक स्ट्रैटेजी पैटर्न भी हो सकती है, यह इस बात पर निर्भर करता है कि आप इसका उपयोग कैसे करते हैं। रणनीति पैटर्न और कमांड पैटर्न के बीच अंतर
रोरी ओ'केन

2
यहाँ जावा 5, 6 के लिए एक बंद कार्यान्वयन है, और 7 mseifed.blogspot.se/2012/09/... यह सब एक के लिए पूछ सकते शामिल है ... मुझे लगता है कि यह बहुत भयानक है!
एमएमएम

@SecretService: वह लिंक मर चुका है।
लॉरेंस Dol

@ लॉरेंसडॉल हाँ, यह है। यहाँ क्लास का एक पास्टबिन है जिसका मैं उपयोग कर रहा हूँ। pastebin.com/b1j3q2Lp
mmm

32

प्रत्येक "फंक्शन पॉइंटर" के लिए, मैं एक छोटा फ़नकार वर्ग बनाऊँगा जो आपकी गणना को लागू करता है। एक इंटरफ़ेस को परिभाषित करें जिसे सभी वर्ग कार्यान्वित करेंगे, और उन वस्तुओं के उदाहरणों को अपने बड़े कार्य में पारित करेंगे। यह " कमांड पैटर्न ", और " रणनीति पैटर्न " का एक संयोजन है ।

@ sblundy का उदाहरण अच्छा है।


28

जब अलग-अलग गणनाओं की एक पूर्वनिर्धारित संख्या होती है तो आप उस एक लाइन में कर सकते हैं, एक एनम का उपयोग करना एक रणनीति पैटर्न को लागू करने का एक त्वरित, अभी तक स्पष्ट तरीका है।

public enum Operation {
    PLUS {
        public double calc(double a, double b) {
            return a + b;
        }
    },
    TIMES {
        public double calc(double a, double b) {
            return a * b;
        }
    }
     ...

     public abstract double calc(double a, double b);
}

जाहिर है, रणनीति पद्धति की घोषणा, साथ ही प्रत्येक कार्यान्वयन का ठीक एक उदाहरण सभी को एक एकल वर्ग / फ़ाइल में परिभाषित किया गया है।


24

आपको एक इंटरफ़ेस बनाने की ज़रूरत है जो फ़ंक्शन (ओं) को प्रदान करता है जिसे आप चारों ओर से गुजरना चाहते हैं। उदाहरण के लिए:

/**
 * A simple interface to wrap up a function of one argument.
 * 
 * @author rcreswick
 *
 */
public interface Function1<S, T> {

   /**
    * Evaluates this function on it's arguments.
    * 
    * @param a The first argument.
    * @return The result.
    */
   public S eval(T a);

}

फिर, जब आपको कोई फ़ंक्शन पास करने की आवश्यकता होती है, तो आप उस इंटरफ़ेस को लागू कर सकते हैं:

List<Integer> result = CollectionUtilities.map(list,
        new Function1<Integer, Integer>() {
           @Override
           public Integer eval(Integer a) {
              return a * a;
           }
        });

अंत में, फंक्शन 1 में दिया गया नक्शा फ़ंक्शन निम्नानुसार उपयोग करता है:

   public static <K,R,S,T> Map<K, R> zipWith(Function2<R,S,T> fn, 
         Map<K, S> m1, Map<K, T> m2, Map<K, R> results){
      Set<K> keySet = new HashSet<K>();
      keySet.addAll(m1.keySet());
      keySet.addAll(m2.keySet());

      results.clear();

      for (K key : keySet) {
         results.put(key, fn.eval(m1.get(key), m2.get(key)));
      }
      return results;
   }

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


4
यह "उत्तर" समाधान सेट की तुलना में निर्धारित समस्या से अधिक संबंधित है
tchrist

18

::ऑपरेटर का उपयोग करते हुए विधि संदर्भ

आप विधि तर्कों में विधि संदर्भ का उपयोग कर सकते हैं जहां विधि एक कार्यात्मक इंटरफ़ेस को स्वीकार करता है । एक कार्यात्मक इंटरफ़ेस कोई भी इंटरफ़ेस है जिसमें केवल एक सार पद्धति होती है। (एक कार्यात्मक इंटरफ़ेस में एक या अधिक डिफ़ॉल्ट विधियाँ या स्थिर विधियाँ हो सकती हैं।)

IntBinaryOperatorएक कार्यात्मक इंटरफ़ेस है। इसकी अमूर्त विधि, applyAsIntदो intएस को इसके मापदंडों के रूप में स्वीकार करती है और एक रिटर्न देती है intMath.maxयह भी दो intएस स्वीकार करता है और एक रिटर्न देता है int। इस उदाहरण में, A.method(Math::max);बनाता है parameter.applyAsIntकरने के लिए अपने दो इनपुट मान भेजने Math.maxऔर उस का परिणाम लौटने Math.max

import java.util.function.IntBinaryOperator;

class A {
    static void method(IntBinaryOperator parameter) {
        int i = parameter.applyAsInt(7315, 89163);
        System.out.println(i);
    }
}
import java.lang.Math;

class B {
    public static void main(String[] args) {
        A.method(Math::max);
    }
}

सामान्य तौर पर, आप इसका उपयोग कर सकते हैं:

method1(Class1::method2);

के बजाय:

method1((arg1, arg2) -> Class1.method2(arg1, arg2));

जो इसके लिए छोटा है:

method1(new Interface1() {
    int method1(int arg1, int arg2) {
        return Class1.method2(arg1, agr2);
    }
});

अधिक जानकारी के लिए, जावा 8 और जावा भाषा विनिर्देश §15.13 में :: (डबल कोलन) ऑपरेटर देखें


15

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

इसका "लाभ" है कि आप एक्सेस प्रतिबंधों को अनदेखा कर सकते हैं और निजी तरीकों को कॉल कर सकते हैं (उदाहरण में नहीं दिखाया गया है, लेकिन आप उन तरीकों को कॉल कर सकते हैं जो कंपाइलर आमतौर पर आपको कॉल नहीं करने देंगे)।

फिर, यह एक दुर्लभ मामला है कि यह समझ में आता है, लेकिन उन अवसरों पर यह एक अच्छा उपकरण है।

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

class Main
{
    public static void main(final String[] argv)
        throws NoSuchMethodException,
               IllegalAccessException,
               IllegalArgumentException,
               InvocationTargetException
    {
        final String methodName;
        final Method method;
        final Main   main;

        main = new Main();

        if(argv.length == 0)
        {
            methodName = "foo";
        }
        else
        {
            methodName = "bar";
        }

        method = Main.class.getDeclaredMethod(methodName, int.class);

        main.car(method, 42);
    }

    private void foo(final int x)
    {
        System.out.println("foo: " + x);
    }

    private void bar(final int x)
    {
        System.out.println("bar: " + x);
    }

    private void car(final Method method,
                     final int    val)
        throws IllegalAccessException,
               IllegalArgumentException,
               InvocationTargetException
    {
        method.invoke(this, val);
    }
}

1
मैं मेनू हैंडलिंग / GUIs के लिए कभी-कभी इसका उपयोग करता हूं क्योंकि विधि सिंटैक्स अनाम आंतरिक श्रेणी सिंटैक्स की तुलना में बहुत सरल है। यह साफ है, लेकिन आप प्रतिबिंब की जटिलता को जोड़ रहे हैं, जिसे कुछ लोग खोदना नहीं चाहते हैं, इसलिए यह सुनिश्चित करें कि आप इसे सही समझें और हर संभावित त्रुटि स्थिति के लिए स्पष्ट, पाठकीय त्रुटियां हैं।
बिल के

आप इसे सामान्य रूप से जेनरिक का उपयोग करके टाइप कर सकते हैं और आपको प्रतिबिंब की आवश्यकता नहीं है।
लुइगी प्लिंज

1
मैं यह देखने में विफल हूं कि जेनरिक का उपयोग करना और प्रतिबिंब का उपयोग नहीं करना क्या आपको स्ट्रिंग में निहित नाम से एक विधि को कॉल करने देगा?
टोफूबीर

1
@LuigiPlinge - क्या आप अपने मतलब के लिए एक कोड स्निपेट प्रदान कर सकते हैं?
टूलमेकरसैट

13

यदि आपके पास सिर्फ एक पंक्ति है जो अलग है तो आप एक पैरामीटर जोड़ सकते हैं जैसे कि एक ध्वज और यदि (ध्वज) कथन जो एक पंक्ति या दूसरा कहता है।


1
javaslook का जवाब ऐसा करने के लिए एक साफ-सुथरे तरीके की तरह लगता है, अगर दो से अधिक गणना संस्करण। या यदि कोई विधि में कोड एम्बेड करना चाहता है, तो विभिन्न मामलों के लिए एक एनम जो विधि को संभालती है, और एक स्विच।
टूलमेकरसेव

1
@ToolmakerSteve सच है, हालांकि आज आप जावा 8 में लैम्ब्डा का उपयोग करेंगे।
पीटर लॉरी

12

जावा को बंद करने के लिए चल रहे काम के बारे में सुनने के लिए भी आपकी रुचि हो सकती है:

जावा में बंद होने की वर्तमान स्थिति क्या है?

http://gafter.blogspot.com/2006/08/closures-for-java.html
http://tech.puredanger.com/java7/#closures


उपयोगी लिंक्स के लिए +1, भले ही मुझे लगता है कि जावा में क्लोजर जोड़ना पूरी तरह से अनहेल्दी है।
मिकेरा

11

ऑपरेटर का उपयोग करके नया जावा 8 कार्यात्मक इंटरफेस और विधि संदर्भ::

जावा 8 " @ फ़ंक्शनल इंटरफ़ेस " बिंदुओं के साथ विधि संदर्भ (MyClass :: नया) बनाए रखने में सक्षम है । समान विधि नाम की कोई आवश्यकता नहीं है, केवल उसी विधि हस्ताक्षर की आवश्यकता है।

उदाहरण:

@FunctionalInterface
interface CallbackHandler{
    public void onClick();
}

public class MyClass{
    public void doClick1(){System.out.println("doClick1");;}
    public void doClick2(){System.out.println("doClick2");}
    public CallbackHandler mClickListener = this::doClick;

    public static void main(String[] args) {
        MyClass myObjectInstance = new MyClass();
        CallbackHandler pointer = myObjectInstance::doClick1;
        Runnable pointer2 = myObjectInstance::doClick2;
        pointer.onClick();
        pointer2.run();
    }
}

तो, हमारे यहाँ क्या है?

  1. फ़ंक्शनल इंटरफ़ेस - यह इंटरफ़ेस है, एनोटेट है या @FunctionalInterface के साथ नहीं है , जिसमें केवल एक विधि घोषणा है।
  2. विधि सन्दर्भ - यह सिर्फ विशेष वाक्यविन्यास है, इस तरह दिखता है, ऑब्जेक्टइन्स्टेंस :: मेथडैम , और कुछ नहीं कुछ कम नहीं।
  3. उपयोग उदाहरण - बस एक असाइनमेंट ऑपरेटर और फिर इंटरफ़ेस विधि कॉल।

आप केवल और केवल उन लोगों के लिए अंतर्राष्ट्रीय इंटरेक्शन का उपयोग कर सकते हैं!

क्योंकि इस तरह के अन्य सभी फ़ंक्शन पॉइंटर्स कोड पठनीयता और समझने की क्षमता के लिए वास्तव में खराब हैं। हालांकि, प्रत्यक्ष विधि संदर्भ कभी-कभी काम आते हैं, उदाहरण के लिए foreach के साथ।

कई पूर्वनिर्धारित कार्यात्मक इंटरफेस हैं:

Runnable              -> void run( );
Supplier<T>           -> T get( );
Consumer<T>           -> void accept(T);
Predicate<T>          -> boolean test(T);
UnaryOperator<T>      -> T apply(T);
BinaryOperator<T,U,R> -> R apply(T, U);
Function<T,R>         -> R apply(T);
BiFunction<T,U,R>     -> R apply(T, U);
//... and some more of it ...
Callable<V>           -> V call() throws Exception;
Readable              -> int read(CharBuffer) throws IOException;
AutoCloseable         -> void close() throws Exception;
Iterable<T>           -> Iterator<T> iterator();
Comparable<T>         -> int compareTo(T);
Comparator<T>         -> int compare(T,T);

पहले के जावा संस्करणों के लिए आपको अमरुद लाइब्रेरी की कोशिश करनी चाहिए, जिसकी कार्यक्षमता और वाक्यविन्यास समान है, जैसा कि एड्रियन पेट्रेस्कु ने ऊपर बताया है।

अतिरिक्त शोध के लिए Java 8 Cheatsheet देखें

और जावा भाषा विनिर्देश 315.13 लिंक के लिए द हेट विथ द हेट के लिए धन्यवाद ।


7
" क्योंकि अन्य सभी ... कोड पठनीयता के लिए वास्तव में खराब हैं " एक पूरी तरह से निराधार दावा है, और इसके अलावा, गलत है।
लॉरेंस Dol

9

@ sblundy का उत्तर बहुत अच्छा है, लेकिन अनाम आंतरिक वर्गों में दो छोटी खामियां हैं, प्राथमिक यह है कि वे पुन: प्रयोज्य नहीं होते हैं और माध्यमिक एक भारी वाक्यविन्यास है।

अच्छी बात यह है कि उनका पैटर्न मुख्य वर्ग (गणना प्रदर्शन करने वाले) में किसी भी बदलाव के बिना पूरी कक्षाओं में फैलता है।

जब आप किसी नए वर्ग को तुरंत भेजते हैं, तो आप उस वर्ग में मापदंडों को पारित कर सकते हैं जो आपके समीकरण में स्थिरांक के रूप में कार्य कर सकते हैं - इसलिए यदि आपका कोई आंतरिक वर्ग इस तरह दिखता है:

f(x,y)=x*y

लेकिन कभी-कभी आपको एक की आवश्यकता होती है:

f(x,y)=x*y*2

और शायद एक तिहाई है:

f(x,y)=x*y/2

दो अनाम आंतरिक वर्गों को बनाने या "passthrough" पैरामीटर को जोड़ने के बजाय, आप एक एकल गतिविधि वर्ग बना सकते हैं जिसे आप निम्न रूप से लिखते हैं:

InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f=new InnerFunc(2.0);// for the second
calculateUsing(f);
f=new InnerFunc(0.5);// for the third
calculateUsing(f);

यह बस कक्षा में स्थिरांक को संग्रहीत करेगा और इसे इंटरफ़ेस में निर्दिष्ट विधि में उपयोग करेगा।

वास्तव में, अगर पता है कि आपका फ़ंक्शन संग्रहीत / पुन: उपयोग नहीं किया जाएगा, तो आप ऐसा कर सकते हैं:

InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f.setConstant(2.0);
calculateUsing(f);
f.setConstant(0.5);
calculateUsing(f);

लेकिन अपरिवर्तनीय कक्षाएं सुरक्षित हैं - मैं इस तरह के उत्परिवर्तनीय वर्ग बनाने के लिए औचित्य के साथ नहीं आ सकता।

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


है ना? ओपी विभिन्न गणनाओं (एल्गोरिदम; तर्क) के बारे में बात कर रहा है ; आप अलग-अलग मान (डेटा) दिखा रहे हैं । आप एक विशिष्ट मामले को दिखाते हैं जहां अंतर को एक मूल्य में शामिल किया जा सकता है, लेकिन यह समस्या का एक अनुचित सरलीकरण है।
टूलमेकरसेव

6

Google अमरूद पुस्तकालय , जो बहुत लोकप्रिय हो रहे हैं, में एक सामान्य कार्य और विधेय वस्तु है जो उन्होंने अपने एपीआई के कई हिस्सों में काम किया है।


यदि यह कोड विवरण देता है तो यह उत्तर अधिक उपयोगी होगा। स्वीकार किए गए उत्तर में दिखाए गए कोड को लें, और यह दिखाएं कि यह फ़ंक्शन का उपयोग कैसे दिखेगा।
टूलमेकरसैट

4

मेरे लिए एक रणनीति पैटर्न की तरह लगता है। Fluffycat.com जावा पैटर्न देखें।


4

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

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

जैसा कि मैंने इसके बारे में अधिक सोचा था, मुझे एहसास हुआ कि ओओपी प्रतिमान में एक कॉलबैक को एक वस्तु और एक विधि को एक साथ बांधने की आवश्यकता होती है - कॉलबैक ऑब्जेक्ट दर्ज करें।

जावा में कॉलबैक के लिए मेरा प्रतिबिंब आधारित समाधान देखें । किसी भी उपयोग के लिए नि: शुल्क।


4

ओ.के., यह धागा पहले से ही काफी पुराना है, इसलिए शायद मेरा उत्तर प्रश्न के लिए उपयोगी नहीं है। लेकिन जब से इस धागे ने मुझे अपना समाधान खोजने में मदद की, मैं इसे यहाँ वैसे भी रख दूँगा।

मुझे ज्ञात इनपुट और ज्ञात आउटपुट (दोनों डबल ) के साथ एक चर स्थिर विधि का उपयोग करने की आवश्यकता थी । तो, विधि पैकेज और नाम जानने के बाद, मैं निम्नानुसार काम कर सकता हूं:

java.lang.reflect.Method Function = Class.forName(String classPath).getMethod(String method, Class[] params);

एक फ़ंक्शन के लिए जो पैरामीटर के रूप में एक डबल स्वीकार करता है।

इसलिए, अपनी ठोस स्थिति में मैंने इसके साथ शुरुआत की

java.lang.reflect.Method Function = Class.forName("be.qan.NN.ActivationFunctions").getMethod("sigmoid", double.class);

और बाद में इसे और अधिक जटिल स्थिति में लागू किया

return (java.lang.Double)this.Function.invoke(null, args);

java.lang.Object[] args = new java.lang.Object[] {activity};
someOtherFunction() + 234 + (java.lang.Double)Function.invoke(null, args);

जहां गतिविधि एक मनमाना दोहरा मूल्य है। मैं शायद इसे थोड़ा और अमूर्त करने और इसे सामान्य करने के बारे में सोच रहा हूं, जैसा कि SoftwareMonkey ने किया है, लेकिन वर्तमान में मैं इसके तरीके से काफी खुश हूं। कोड की तीन लाइनें, कोई वर्ग और आवश्यक इंटरफेस, यह बहुत बुरा नहीं है।



4

कार्यों की एक सरणी के लिए इंटरफेस के बिना एक ही काम करने के लिए:

class NameFuncPair
{
    public String name;                // name each func
    void   f(String x) {}              // stub gets overridden
    public NameFuncPair(String myName) { this.name = myName; }
}

public class ArrayOfFunctions
{
    public static void main(String[] args)
    {
        final A a = new A();
        final B b = new B();

        NameFuncPair[] fArray = new NameFuncPair[]
        {
            new NameFuncPair("A") { @Override void f(String x) { a.g(x); } },
            new NameFuncPair("B") { @Override void f(String x) { b.h(x); } },
        };

        // Go through the whole func list and run the func named "B"
        for (NameFuncPair fInstance : fArray)
        {
            if (fInstance.name.equals("B"))
            {
                fInstance.f(fInstance.name + "(some args)");
            }
        }
    }
}

class A { void g(String args) { System.out.println(args); } }
class B { void h(String args) { System.out.println(args); } }

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

3

लम्‍बडाज देखें

http://code.google.com/p/lambdaj/

और विशेष रूप से इसकी नई बंद करने की सुविधा

http://code.google.com/p/lambdaj/wiki/Closures

और आप अर्थहीन इंटरफ़ेस बनाने या बदसूरत भीतरी वर्गों का उपयोग किए बिना क्लोजर या फ़ंक्शन पॉइंटर को परिभाषित करने के लिए एक बहुत ही पठनीय तरीका पाएंगे


3

वाह, क्यों न केवल एक डेलीगेट वर्ग बनाया जाए, जो इतना कठिन न हो कि मैंने पहले से ही जावा के लिए किया हो और इसे पैरामीटर में पास करने के लिए उपयोग किया जाए जहां टी वापसी प्रकार है। मुझे खेद है लेकिन सामान्य रूप से जावा सीखने में C ++ / C # प्रोग्रामर के रूप में, मुझे फंक्शन पॉइंटर्स की आवश्यकता है क्योंकि वे बहुत काम आते हैं। यदि आप किसी भी वर्ग से परिचित हैं जो विधि सूचना से संबंधित है तो आप इसे कर सकते हैं। जावा पुस्तकालयों में जो java.lang.reflect.method होगा।

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


जब तक आप कोड विवरण नहीं दिखाते हैं, तब तक कोई उपयोगी उत्तर नहीं। डेलीगेट क्लास बनाने से कैसे मदद मिलती है? प्रति विकल्प के लिए क्या कोड आवश्यक है?
टूलमेकरसैट

2

जावा 8 में से किसी भी उत्तर ने पूर्ण, सामंजस्यपूर्ण उदाहरण नहीं दिया है, इसलिए यहां यह आता है।

"फ़ंक्शन पॉइंटर" को स्वीकार करने वाली विधि को निम्नानुसार घोषित करें:

void doCalculation(Function<Integer, String> calculation, int parameter) {
    final String result = calculation.apply(parameter);
}

इसे लंबोदर अभिव्यक्ति के साथ फ़ंक्शन प्रदान करके कॉल करें:

doCalculation((i) -> i.toString(), 2);

1

यदि कोई ऐसे कार्य को पारित करने के लिए संघर्ष कर रहा है जो अपने व्यवहार को परिभाषित करने के लिए मापदंडों का एक सेट लेता है, लेकिन उस योजना को लागू करने के लिए मापदंडों का एक और सेट, जिस पर अमल करता है:

(define (function scalar1 scalar2)
  (lambda (x) (* x scalar1 scalar2)))

जावा में पैरामीटर-परिभाषित व्यवहार के साथ पास फ़ंक्शन देखें


1

Java8 के बाद से, आप लैम्ब्डा का उपयोग कर सकते हैं, जिसमें आधिकारिक SE 8 API में लाइब्रेरी भी हैं।

उपयोग: आपको केवल एक सार पद्धति के साथ एक इंटरफ़ेस का उपयोग करने की आवश्यकता है। इसका एक उदाहरण बनाएं (आप पहले से उपलब्ध कराए गए एक जावा SE 8 का उपयोग करना चाह सकते हैं):

Function<InputType, OutputType> functionname = (inputvariablename) {
... 
return outputinstance;
}

अधिक जानकारी के लिए दस्तावेज़ देखें: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html


1

जावा 8 से पहले, फ़ंक्शन-पॉइंटर-जैसी कार्यक्षमता के लिए निकटतम विकल्प एक अनाम वर्ग था। उदाहरण के लिए:

Collections.sort(list, new Comparator<CustomClass>(){
    public int compare(CustomClass a, CustomClass b)
    {
        // Logic to compare objects of class CustomClass which returns int as per contract.
    }
});

लेकिन अब जावा 8 में हमारे पास लैम्ब्डा एक्सप्रेशन के रूप में जाना जाने वाला एक बहुत ही साफ-सुथरा विकल्प है , जिसका उपयोग इस प्रकार किया जा सकता है:

list.sort((a, b) ->  { a.isBiggerThan(b) } );

जहां isBiggerThan में एक विधि है CustomClass। हम यहां विधि संदर्भ का भी उपयोग कर सकते हैं:

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