:: (जावा में डबल कोलन) ऑपरेटर 8


955

मैं जावा 8 स्रोत की खोज कर रहा था और कोड के इस विशेष भाग को बहुत आश्चर्यचकित पाया:

//defined in IntPipeline.java
@Override
public final OptionalInt reduce(IntBinaryOperator op) {
    return evaluate(ReduceOps.makeInt(op));
}

@Override
public final OptionalInt max() {
    return reduce(Math::max); //this is the gotcha line
}

//defined in Math.java
public static int max(int a, int b) {
    return (a >= b) ? a : b;
}

है Math::maxएक विधि सूचक की तरह कुछ? एक सामान्य staticविधि किस प्रकार परिवर्तित हो जाती है IntBinaryOperator?


60
यह संकलित चीनी है जो आपके द्वारा प्रदान किए गए फ़ंक्शन (मौजूदा कोड बेस के साथ उपयोग करने के लिए पूरे लैम्ब्डा चीज़ को आसान बनाने के लिए) के आधार पर संकलक ऑटो-जनरेट इंटरफ़ेस कार्यान्वयन है।
नीट

4
java.dzone.com/articles/java-lambda-expressions-vs मदद कर सकता है, इस विषय में गहराई से नहीं देखा
पोंटस बैकलंड

8
@ नहीं, यह "सिंटैक्टिक शुगर" नहीं है, जब तक कि आप क्या नहीं कह सकते। यानी "x y के लिए वाक्य रचना चीनी है"।
इंगो

6
@ यदि मैं इसका उपयोग करने पर हर बार लैम्बडा की एक नई वस्तु बनाता हूं। TestingLambda$$Lambda$2/8460669और TestingLambda$$Lambda$3/11043253दो इनवोकेशन पर बनाए गए थे।
नरेंद्र पथाई

13
लम्बदास और विधि संदर्भ "सादे पुराने अनाम आंतरिक वर्ग" नहीं हैं। देखें programmers.stackexchange.com/a/181743/59134 । हां, यदि आवश्यक हो, तो नए वर्ग और उदाहरण बनते हैं, यदि आवश्यक हो, लेकिन केवल यदि आवश्यक हो तो।
स्टुअर्ट मार्क्स

जवाबों:


1022

आमतौर पर, एक इस reduceपद्धति का उपयोग करके कॉल करेगा Math.max(int, int):

reduce(new IntBinaryOperator() {
    int applyAsInt(int left, int right) {
        return Math.max(left, right);
    }
});

बस कॉल करने के लिए बहुत सारे सिंटैक्स की आवश्यकता होती है Math.max। यहीं से लंबोदर भाव क्रीड़ा में आते हैं। जावा 8 के बाद से इसे बहुत ही छोटे तरीके से करने की अनुमति है:

reduce((int left, int right) -> Math.max(left, right));

यह कैसे काम करता है? जावा संकलक "पता लगाता है", कि आप एक ऐसी विधि को लागू करना चाहते हैं जो दो intएस को स्वीकार करता है और एक को वापस करता है int। यह इंटरफ़ेस के एक और एकमात्र विधि IntBinaryOperator(जिस पद्धति को reduceआप कॉल करना चाहते हैं) के औपचारिक मापदंडों के बराबर है । इसलिए कंपाइलर आपके लिए बाकी काम करता है - यह मानता है कि आप इसे लागू करना चाहते हैं IntBinaryOperator

लेकिन जैसा कि Math.max(int, int)खुद की औपचारिक आवश्यकताओं को पूरा करता है IntBinaryOperator, इसे सीधे इस्तेमाल किया जा सकता है। क्योंकि जावा 7 में कोई सिंटैक्स नहीं है जो एक विधि को एक तर्क के रूप में पारित करने की अनुमति देता है (आप केवल विधि परिणाम पास कर सकते हैं, लेकिन कभी भी विधि संदर्भ नहीं), ::सिंटैक्स को जावा 8 में संदर्भ विधियों में पेश किया गया था:

reduce(Math::max);

ध्यान दें कि यह संकलक द्वारा व्याख्या की जाएगी, जेवीएम द्वारा रनटाइम पर नहीं! यद्यपि यह सभी तीन कोड स्निपेट के लिए अलग-अलग बायोटेक का उत्पादन करता है, वे शब्दार्थ समान हैं, इसलिए पिछले दो को IntBinaryOperatorऊपर कार्यान्वयन के कम (और शायद अधिक कुशल) संस्करण माना जा सकता है!

( लैंबडा एक्सप्रेशंस का अनुवाद भी देखें )


489

::विधि संदर्भ कहा जाता है। यह मूल रूप से एकल विधि का संदर्भ है। यानी यह नाम से मौजूदा पद्धति को संदर्भित करता है।

लघु स्पष्टीकरण :
नीचे एक स्थिर विधि के संदर्भ का एक उदाहरण है:

class Hey {
     public static double square(double num){
        return Math.pow(num, 2);
    }
}

Function<Double, Double> square = Hey::square;
double ans = square.apply(23d);

squareऑब्जेक्ट रेफरेंस की तरह ही पास हो सकता है और जरूरत पड़ने पर ट्रिगर हो सकता है। वास्तव में, यह वस्तुओं के "सामान्य" तरीकों के संदर्भ के रूप में आसानी से उपयोग किया जा सकता staticहै। उदाहरण के लिए:

class Hey {
    public double square(double num) {
        return Math.pow(num, 2);
    }
}

Hey hey = new Hey();
Function<Double, Double> square = hey::square;
double ans = square.apply(23d);

Functionऊपर एक कार्यात्मक इंटरफ़ेस है । पूरी तरह से समझने के लिए ::, कार्यात्मक इंटरफेस को भी समझना महत्वपूर्ण है। पूरी तरह से, एक कार्यात्मक इंटरफ़ेस सिर्फ एक सार पद्धति वाला एक इंटरफ़ेस है।

कार्यात्मक इंटरफेस के उदाहरणों में शामिल Runnable, Callable, और ActionListener

Functionऊपर सिर्फ एक विधि के साथ एक कार्यात्मक इंटरफ़ेस है apply:। यह एक तर्क लेता है और एक परिणाम उत्पन्न करता है।


कारण यह ::है कि भयानक हैं :

मेथड रेफरेंस ऐसे एक्सप्रेशन होते हैं जिनका लैम्बडा एक्सप्रेशन (...) के समान उपचार होता है, लेकिन वे एक मेथड बॉडी प्रदान करने के बजाय, मौजूदा विधि को नाम से संदर्भित करते हैं।

जैसे कि मेमने का शरीर लिखने के बजाय

Function<Double, Double> square = (Double x) -> x * x;

आप बस कर सकते हैं

Function<Double, Double> square = Hey::square;

रनटाइम के दौरान, ये दो squareविधियां बिल्कुल एक-दूसरे के समान व्यवहार करती हैं। बाईटेकोड समान हो सकता है या नहीं (हालांकि, उपरोक्त मामले के लिए, एक ही बायटेकोड उत्पन्न होता है, ऊपर संकलित करें और साथ जांचें javap -c)।

संतुष्ट करने के लिए एकमात्र मुख्य मानदंड है: आपके द्वारा प्रदान की जाने वाली विधि में ऑब्जेक्ट संदर्भ के रूप में आपके द्वारा उपयोग किए जाने वाले कार्यात्मक इंटरफ़ेस की विधि के समान हस्ताक्षर होना चाहिए

नीचे अवैध है:

Supplier<Boolean> p = Hey::square; // illegal

squareएक तर्क की उम्मीद करता है और एक रिटर्न देता है doublegetमें विधि प्रदायक एक मान देता है लेकिन एक तर्क नहीं लेता है। इस प्रकार, यह एक त्रुटि का परिणाम है।

एक विधि संदर्भ एक कार्यात्मक इंटरफ़ेस की विधि को संदर्भित करता है। (जैसा कि उल्लेख किया गया है, कार्यात्मक इंटरफेस में केवल एक ही विधि हो सकती है)।

कुछ और उदाहरण: उपभोक्ता का acceptतरीका एक इनपुट लेता है, लेकिन कुछ भी वापस नहीं करता है।

Consumer<Integer> b1 = System::exit;   // void exit(int status)
Consumer<String[]> b2 = Arrays::sort;  // void sort(Object[] a)
Consumer<String> b3 = MyProgram::main; // void main(String... args)

class Hey {
    public double getRandom() {
        return Math.random();
    }
}

Callable<Double> call = hey::getRandom;
Supplier<Double> call2 = hey::getRandom;
DoubleSupplier sup = hey::getRandom;
// Supplier is functional interface that takes no argument and gives a result

ऊपर, getRandomकोई तर्क नहीं लेता है और एक रिटर्न देता है double। तो कोई भी कार्यात्मक इंटरफ़ेस जो मानदंड को संतुष्ट करता है: कोई तर्क नहीं लेता है और वापसीdouble का उपयोग किया जा सकता है।

एक और उदाहरण:

Set<String> set = new HashSet<>();
set.addAll(Arrays.asList("leo","bale","hanks"));
Predicate<String> pred = set::contains;
boolean exists = pred.test("leo");

पैरामीटर किए गए प्रकारों के मामले में :

class Param<T> {
    T elem;
    public T get() {
        return elem;
    }

    public void set(T elem) {
        this.elem = elem;
    }

    public static <E> E returnSame(E elem) {
        return elem;
    }
}

Supplier<Param<Integer>> obj = Param<Integer>::new;
Param<Integer> param = obj.get();
Consumer<Integer> c = param::set;
Supplier<Integer> s = param::get;

Function<String, String> func = Param::<String>returnSame;

विधि संदर्भों की अलग-अलग शैलियाँ हो सकती हैं, लेकिन मूल रूप से इन सभी का मतलब एक ही है और इसे केवल लंबोदर के रूप में देखा जा सकता है:

  1. एक स्थिर विधि ( ClassName::methName)
  2. किसी विशेष वस्तु की एक आवृत्ति विधि ( instanceRef::methName)
  3. किसी विशेष वस्तु की एक सुपर विधि ( super::methName)
  4. किसी विशेष प्रकार की मनमानी वस्तु का एक उदाहरण विधि ( ClassName::methName)
  5. एक क्लास कंस्ट्रक्टर संदर्भ ( ClassName::new)
  6. एक सरणी निर्माता संदर्भ ( TypeName[]::new)

अधिक संदर्भ के लिए, http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html देखें ।


6
विवरण के लिए आपका धन्यवाद। सारांश में: '::' एक फंक्शन्स को निकालने के लिए उपयोग करता है जो कि एक फंक्शनल इन्टरफेस (लैम्बडा) को संतुष्ट करता है: ClassX :: staticMethodX, या instX :: instMethodX "
jessarah

55

हाँ वह सच है। ::ऑपरेटर विधि संदर्भ देने के लिए प्रयोग किया जाता है। तो, कोई कक्षाओं से स्थिर तरीकों को निकाल सकता है या वस्तुओं से तरीकों का उपयोग कर सकता है। कंस्ट्रक्टरों के लिए भी एक ही ऑपरेटर का उपयोग किया जा सकता है। यहां उल्लिखित सभी मामलों को नीचे दिए गए कोड नमूने में उदाहरण दिया गया है।

ओरेकल से आधिकारिक दस्तावेज यहां पाया जा सकता है

आप इस लेख में JDK 8 परिवर्तनों का बेहतर अवलोकन कर सकते हैं । में विधि / निर्माता को संदर्भित अनुभाग एक कोड उदाहरण भी प्रदान की जाती है:

interface ConstructorReference {
    T constructor();
}

interface  MethodReference {
   void anotherMethod(String input);
}

public class ConstructorClass {
    String value;

   public ConstructorClass() {
       value = "default";
   }

   public static void method(String input) {
      System.out.println(input);
   }

   public void nextMethod(String input) {
       // operations
   }

   public static void main(String... args) {
       // constructor reference
       ConstructorReference reference = ConstructorClass::new;
       ConstructorClass cc = reference.constructor();

       // static method reference
       MethodReference mr = cc::method;

       // object method reference
       MethodReference mr2 = cc::nextMethod;

       System.out.println(cc.value);
   }
}

एक अच्छी व्याख्या यहाँ पाई गई है: doanduyhai.wordpress.com/2012/07/14/…
Olimpiu POP

1
@RichardTingle method(Math::max);मंगलाचरण है और विधि की परिभाषा की तरह होगा public static void method(IntBinaryOperator op){System.out.println(op.applyAsInt(1, 2));}। इस प्रकार इसका उपयोग किया जाता है।
नरेंद्र पथाई

2
C # से परिचित लोगों के लिए यह DelegateType d = new DelegateType (MethodName) के समान है;
एड्रियन ज़ैनस्कु

27

यह थोड़ा देर से लगता है लेकिन यहाँ मेरे दो सेंट हैं। अनाम विधियों को बनाने के लिए एक लैम्ब्डा अभिव्यक्ति का उपयोग किया जाता है। यह एक मौजूदा पद्धति के अलावा कुछ भी नहीं करता है, लेकिन इसके नाम से सीधे विधि को संदर्भित करना स्पष्ट है। और विधि संदर्भ हमें विधि-संदर्भ ऑपरेटर का उपयोग करने में सक्षम बनाता है ::

निम्नलिखित सरल वर्ग पर विचार करें जहां प्रत्येक कर्मचारी का नाम और ग्रेड है।

public class Employee {
    private String name;
    private String grade;

    public Employee(String name, String grade) {
        this.name = name;
        this.grade = grade;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGrade() {
        return grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }
}

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

    List<Employee> employeeList = getDummyEmployees();

    // Using anonymous class
    employeeList.sort(new Comparator<Employee>() {
           @Override
           public int compare(Employee e1, Employee e2) {
               return e1.getGrade().compareTo(e2.getGrade());
           }
    });

जहां getDummyEmployee () कुछ विधि है:

private static List<Employee> getDummyEmployees() {
        return Arrays.asList(new Employee("Carrie", "C"),
                new Employee("Fanishwar", "F"),
                new Employee("Brian", "B"),
                new Employee("Donald", "D"),
                new Employee("Adam", "A"),
                new Employee("Evan", "E")
                );
    }

अब हम जानते हैं कि तुलनित्र एक कार्यात्मक इंटरफ़ेस है। एक कार्यात्मक इंटरफ़ेस (हालांकि यह एक या अधिक डिफ़ॉल्ट या स्थिर तरीकों हो सकती है) ठीक एक सार विधि के साथ एक है। लैम्ब्डा अभिव्यक्ति प्रदान करती है @FunctionalInterfaceइसलिए एक कार्यात्मक इंटरफ़ेस में केवल एक सार विधि हो सकती है। हम लैम्ब्डा अभिव्यक्ति का उपयोग इस प्रकार कर सकते हैं:

employeeList.sort((e1,e2) -> e1.getGrade().compareTo(e2.getGrade())); // lambda exp

यह सब अच्छा लगता है लेकिन क्या होगा यदि वर्ग Employeeभी समान विधि प्रदान करता है:

public class Employee {
    private String name;
    private String grade;
    // getter and setter
    public static int compareByGrade(Employee e1, Employee e2) {
        return e1.grade.compareTo(e2.grade);
    }
}

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

employeeList.sort(Employee::compareByGrade); // method reference

डॉक्स के अनुसार विधि संदर्भ के चार प्रकार हैं:

+----+-------------------------------------------------------+--------------------------------------+
|    | Kind                                                  | Example                              |
+----+-------------------------------------------------------+--------------------------------------+
| 1  | Reference to a static method                          | ContainingClass::staticMethodName    |
+----+-------------------------------------------------------+--------------------------------------+
| 2  |Reference to an instance method of a particular object | containingObject::instanceMethodName | 
+----+-------------------------------------------------------+--------------------------------------+
| 3  | Reference to an instance method of an arbitrary object| ContainingType::methodName           |
|    | of a particular type                                  |                                      |  
+----+-------------------------------------------------------+--------------------------------------+
| 4  |Reference to a constructor                             | ClassName::new                       |
+------------------------------------------------------------+--------------------------------------+

25

::जावा 8 में शामिल एक नया ऑपरेटर है जिसका उपयोग मौजूदा कक्षा की एक विधि को संदर्भित करने के लिए किया जाता है। आप एक वर्ग के स्थिर तरीकों और गैर-स्थिर तरीकों का उल्लेख कर सकते हैं।

स्थिर विधियों का संदर्भ देने के लिए, वाक्यविन्यास है:

ClassName :: methodName 

गैर-स्थैतिक तरीकों का संदर्भ देने के लिए, वाक्यविन्यास है

objRef :: methodName

तथा

ClassName :: methodName

किसी विधि को संदर्भित करने के लिए एकमात्र शर्त यह है कि विधि एक कार्यात्मक इंटरफ़ेस में मौजूद है, जिसे विधि संदर्भ के साथ संगत होना चाहिए।

विधि संदर्भ, जब मूल्यांकन किया जाता है, तो कार्यात्मक इंटरफ़ेस का एक उदाहरण बनाएं।

इस पर मिला: http://www.speakingcs.com/2014/08/method-references-in-java-8.html


22

यह जावा 8 में एक विधि संदर्भ है। ओरेकल प्रलेखन यहाँ है

जैसा कि प्रलेखन में कहा गया है ...

विधि संदर्भ व्यक्ति :: ComparByAge एक स्टेटिक विधि का संदर्भ है।

निम्नलिखित किसी विशेष वस्तु के उदाहरण विधि के संदर्भ का एक उदाहरण है:

class ComparisonProvider {
    public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
    }

    public int compareByAge(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}

ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName); 

विधि संदर्भ myComparisonProvider :: ComparByName विधि myBparisonProvider का हिस्सा है, जो विधि तुलना का नाम आमंत्रित करता है। JRE विधि प्रकार के तर्कों को प्रभावित करता है, जो इस मामले में (व्यक्ति, व्यक्ति) हैं।


2
लेकिन 'तुलनाबेज' विधि स्थिर नहीं है।
अब्बास

3
@abbas Nor की तुलना तुलनानाम से की जाती है। इसलिए, आप ऑब्जेक्ट का उपयोग करके संदर्भ ऑपरेटर के माध्यम से इन गैर-स्थिर तरीकों तक पहुंचते हैं। यदि वे स्थिर होते, तो आप तुलनात्मक नाम जैसे वर्ग नाम का उपयोग कर सकते थे :: someStaticMethod
शेषाद्री आर

6

:: विधि संदर्भों के लिए ऑपरेटर को जावा 8 में पेश किया गया था। एक विधि संदर्भ एक लैम्ब्डा अभिव्यक्ति के लिए शॉर्टहैंड सिंटैक्स है जो सिर्फ एक विधि को निष्पादित करता है। यहां विधि संदर्भ का सामान्य सिंटैक्स दिया गया है:

Object :: methodName

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

Consumer<String> c = s -> System.out.println(s);

कोड को स्पष्ट करने के लिए, आप उस लंबोदर अभिव्यक्ति को विधि संदर्भ में बदल सकते हैं:

Consumer<String> c = System.out::println;

3

The :: विधि संदर्भ के रूप में जाना जाता है। हम कहते हैं कि हम वर्ग खरीद की गणना पद्धति को कॉल करना चाहते हैं। तब हम इसे इस प्रकार लिख सकते हैं:

Purchase::calculatePrice

इसे लंबोदर अभिव्यक्ति लिखने के संक्षिप्त रूप के रूप में भी देखा जा सकता है क्योंकि विधि संदर्भों को लंबोदर अभिव्यक्तियों में परिवर्तित किया जाता है।


क्या मैं नेस्टेड विधि संदर्भ बना सकता हूं? जैसे

आप उस तरह से एक नेस्टेड विधि संदर्भ नहीं बना सकते हैं
किर्बी

3

मुझे यह स्रोत बहुत दिलचस्प लगा।

वास्तव में, यह लैम्ब्डा है जो एक डबल कोलोन में बदल जाता है । डबल कोलोन अधिक पठनीय है। हम उन चरणों का पालन करते हैं:

चरण 1:

// We create a comparator of two persons
Comparator c = (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge());

चरण 2:

// We use the interference
Comparator c = (p1, p2) -> p1.getAge().compareTo(p2.getAge());

चरण 3:

// The magic using method reference
Comparator c = Comparator.comparing(Person::getAge);

3
लगता है जैसे Person::getAge()होना चाहिए Person::getAge
Qwertiy

2

return reduce(Math::max);करने के लिए आवश्यक नहीं हैreturn reduce(max());

लेकिन इसका मतलब है, कुछ इस तरह:

IntBinaryOperator myLambda = (a, b)->{(a >= b) ? a : b};//56 keystrokes I had to type -_-
return reduce(myLambda);

यदि आप इस तरह लिखते हैं तो आप केवल 47 कीस्ट्रोक बचा सकते हैं

return reduce(Math::max);//Only 9 keystrokes ^_^

2

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

BinaryOperator<TestObject> binary = new BinaryOperator<TestObject>() {

        @Override
        public TestObject apply(TestObject t, TestObject u) {

            return t;
        }
    };

जैसा कि आप गुमनाम कार्यान्वयन में देखते हैं इसके लिए दो TestObject तर्क की आवश्यकता होती है और एक TestObject ऑब्जेक्ट को भी लौटाता है। उपयोग करके इस स्थिति को संतुष्ट करने के लिए::ऑपरेटर हम स्थैतिक विधि से शुरू कर सकते हैं:

public class TestObject {


    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

और फिर कॉल करें:

BinaryOperator<TestObject> binary = TestObject::testStatic;

ठीक है संकलित ठीक है। अगर हमें इंस्टेंस मेथड की जरूरत है तो क्या होगा? उदाहरण विधि के साथ TestObject को अपडेट करें:

public class TestObject {

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

अब हम नीचे दिए गए उदाहरण तक पहुँच सकते हैं:

TestObject testObject = new TestObject();
BinaryOperator<TestObject> binary = testObject::testInstance;

यह कोड ठीक संकलित करता है, लेकिन नीचे एक नहीं:

BinaryOperator<TestObject> binary = TestObject::testInstance;

मेरा ग्रहण मुझे बताओ "TestObject प्रकार से गैर-स्थिर विधि testInstance (TestObject, TestObject) का स्थैतिक संदर्भ नहीं बनाया जा सकता ..."

निष्पक्ष इसकी एक पर्याप्त विधि है, लेकिन अगर हम testInstanceनीचे के रूप में ओवरलोड करते हैं:

public class TestObject {

    public final TestObject testInstance(TestObject t){
        return t;
    }

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

और कॉल करें:

BinaryOperator<TestObject> binary = TestObject::testInstance;

कोड ठीक संकलन करेगा। क्योंकि यह testInstanceडबल पैरामीटर के बजाय सिंगल पैरामीटर के साथ कॉल करेगा । ठीक है तो हमारे दो पैरामीटर क्या हुआ? प्रिंटआउट देखें और देखें:

public class TestObject {

    public TestObject() {
        System.out.println(this.hashCode());
    }

    public final TestObject testInstance(TestObject t){
        System.out.println("Test instance called. this.hashCode:" 
    + this.hashCode());
        System.out.println("Given parameter hashCode:" + t.hashCode());
        return t;
    }

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

जो आउटपुट देगा:

 1418481495  
 303563356  
 Test instance called. this.hashCode:1418481495
 Given parameter hashCode:303563356

ठीक है, इसलिए JVM param1.testInstance (param2) को कॉल करने के लिए पर्याप्त स्मार्ट है। क्या हम testInstanceदूसरे संसाधन से उपयोग कर सकते हैं लेकिन टेस्टऑब्जेक्ट नहीं, अर्थात:

public class TestUtil {

    public final TestObject testInstance(TestObject t){
        return t;
    }
}

और कॉल करें:

BinaryOperator<TestObject> binary = TestUtil::testInstance;

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

public class SubTestObject extends TestObject {

    public final TestObject testInstance(TestObject t){
        return t;
    }

}

और कॉल करें:

BinaryOperator<TestObject> binary = SubTestObject::testInstance;

यह भी संकलित नहीं करेगा, संकलक अभी भी स्थैतिक संदर्भ की तलाश करेगा। लेकिन नीचे कोड ठीक संकलित करेगा क्योंकि यह एक परीक्षा है।

public class TestObject {

    public SubTestObject testInstance(Object t){
        return (SubTestObject) t;
    }

}

BinaryOperator<TestObject> binary = TestObject::testInstance;

* मैं अभी पढ़ रहा हूं इसलिए मैंने कोशिश करके देख लिया है कि अगर मैं गलत हूं तो मुझे सुधारने के लिए स्वतंत्र महसूस करें


2

साधारण कार्यों में जावा -8 स्ट्रीम रेड्यूसर में एक फ़ंक्शन है जो इनपुट के रूप में दो मान लेता है और कुछ गणना के बाद परिणाम देता है। यह परिणाम अगले पुनरावृत्ति में खिलाया जाता है।

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


1

रनटाइम में वे बिलकुल वैसा ही व्यवहार करते हैं। बायटेकोड एक समान हो सकता है / नहीं (इनसे ऊपर के लिए, यह एक ही बायटेकोड उत्पन्न करता है (ऊपर कंपेयर करें और जावाप-सी चेक करें;))

रनटाइम के दौरान वे बिलकुल वैसा ही व्यवहार करते हैं। (गणित :: अधिकतम) ;, यह एक ही गणित उत्पन्न करता है (ऊपर जिप्पी और चेक जावप -सी;);


1

पुराने संस्करणों में, "::" या लैम्बड के बजाय, आप उपयोग कर सकते हैं:

public interface Action {
    void execute();
}

public class ActionImpl implements Action {

    @Override
    public void execute() {
        System.out.println("execute with ActionImpl");
    }

}

public static void main(String[] args) {
    Action action = new Action() {
        @Override
        public void execute() {
            System.out.println("execute with anonymous class");
        }
    };
    action.execute();

    //or

    Action actionImpl = new ActionImpl();
    actionImpl.execute();
}

या विधि को पास करना:

public static void doSomething(Action action) {
    action.execute();
}

1

इसलिए मैं यहां उन जवाबों के टन देखता हूं जो स्पष्ट रूप से अतिरंजित हैं, और यह एक समझ है।

इसका उत्तर बहुत सरल है: :: इसे मेथड रिफरेंस कहा जाता है https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

इसलिए मैं कॉपी-पेस्ट नहीं करूंगा, लिंक पर, आप सभी जानकारी पा सकते हैं यदि आप टेबल पर नीचे स्क्रॉल करते हैं।


अब, एक विधि संदर्भ क्या है, इस पर एक नज़र डालते हैं:

A :: B कुछ इनलाइन लैम्बडा एक्सप्रेशन का विकल्प देता है : (params ...) -> AB (परमेस ...)

अपने प्रश्नों के साथ इसे सहसंबंधित करने के लिए, एक जावा लैम्ब्डा अभिव्यक्ति को समझना आवश्यक है। जो कठिन न हो।

एक इनलाइन लैम्ब्डा अभिव्यक्ति एक परिभाषित कार्यात्मक इंटरफ़ेस के समान है (जो एक इंटरफ़ेस है जिसमें 1 से अधिक नहीं और 1 विधि से कम नहीं है) । आइए एक छोटा नज़र डालें कि मेरा क्या मतलब है:

InterfaceX f = (x) -> x*x; 

InterfaceX एक कार्यात्मक इंटरफ़ेस होना चाहिए। किसी भी कार्यात्मक इंटरफ़ेस, उस कंपाइलर के लिए InterfaceX के बारे में एकमात्र महत्वपूर्ण बात यह है कि आप प्रारूप को परिभाषित करते हैं:

इंटरफ़ेसएक्स इनमें से कोई भी हो सकता है:

interface InterfaceX
{
    public Integer callMe(Integer x);
}

या यह

interface InterfaceX
{
    public Double callMe(Integer x);
}

या अधिक सामान्य:

interface InterfaceX<T,U>
{
    public T callMe(U x);
}

आइए पहले प्रस्तुत मामले और इनलाइन लैम्ब्डा अभिव्यक्ति को लेते हैं जिसे हमने पहले परिभाषित किया था।

जावा 8 से पहले, आप इसे इसी तरह परिभाषित कर सकते हैं:

 InterfaceX o = new InterfaceX(){
                     public int callMe (int x, int y) 
                       {
                        return x*x;
                       } };

कार्यात्मक रूप से, यह एक ही बात है। अंतर यह है कि संकलक इसे कैसे मानता है में अधिक है।

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

class Q {
        public static int anyFunction(int x)
             {
                 return x+5;
             } 
        }

चूंकि विधि anyFunctions InterfaceX रूप में एक ही प्रकार के callme , हम एक विधि संदर्भ के साथ उन दो equivalate कर सकते हैं।

हम इसे इस तरह लिख सकते हैं:

InterfaceX o =  Q::anyFunction; 

और यह इसके बराबर है:

InterfaceX o = (x) -> Q.anyFunction(x);

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


1

पिछले उत्तर क्या हैं के बारे में काफी पूर्ण हैं :: विधि संदर्भ करता है। योग करने के लिए, यह निष्पादित किए बिना एक विधि (या निर्माणकर्ता) को संदर्भित करने का एक तरीका प्रदान करता है, और जब मूल्यांकन किया जाता है, तो यह कार्यात्मक इंटरफ़ेस का एक उदाहरण बनाता है जो लक्ष्य प्रकार का संदर्भ प्रदान करता है।

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


के उपयोग के बिना ::

import java.util.*;

class MyClass {
    private int val;
    MyClass (int v) { val = v; }
    int getVal() { return val; }
}

class ByVal implements Comparator<MyClass> {
    // no need to create this class when using method reference
    public int compare(MyClass source, MyClass ref) {
        return source.getVal() - ref.getVal();
    }
}

public class FindMaxInCol {
    public static void main(String args[]) {
        ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
        myClassList.add(new MyClass(1));
        myClassList.add(new MyClass(0));
        myClassList.add(new MyClass(3));
        myClassList.add(new MyClass(6));

        MyClass maxValObj = Collections.max(myClassList, new ByVal());
    }
}

इसके उपयोग से ::

import java.util.*;

class MyClass {
    private int val;
    MyClass (int v) { val = v; }
    int getVal() { return val; }
}

public class FindMaxInCol {
    static int compareMyClass(MyClass source, MyClass ref) {
        // This static method is compatible with the compare() method defined by Comparator. 
        // So there's no need to explicitly implement and create an instance of Comparator like the first example.
        return source.getVal() - ref.getVal();
    }

    public static void main(String args[]) {
        ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
        myClassList.add(new MyClass(1));
        myClassList.add(new MyClass(0));
        myClassList.add(new MyClass(3));
        myClassList.add(new MyClass(6));

        MyClass maxValObj = Collections.max(myClassList, FindMaxInCol::compareMyClass);
    }
}

-1

डबल कोलन अर्थात ::ऑपरेटर को जावा 8 में विधि संदर्भ के रूप में पेश किया जाता है । मेथड रेफरेंस लैम्ब्डा एक्सप्रेशन का एक रूप है जिसका उपयोग मौजूदा विधि को उसके नाम से संदर्भित करने के लिए किया जाता है।

classname :: methodName

उदाहरण के लिए: -

  • stream.forEach(element -> System.out.println(element))

डबल कोलोन का उपयोग करके ::

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