Java8

2015-01-03 8 views
13

में विभिन्न मानकों के साथ विधियों का संदर्भ मैं सोच रहा हूं कि विधि संदर्भ और कार्यात्मक इंटरफेस के साथ ये सभी चीज़ें निम्न स्तर पर कैसे काम करती हैं। सबसे आसान उदाहरण है, जहां हम कुछ सूचीJava8

List<String> list = new ArrayList<>(); 
list.add("b"); 
list.add("a"); 
list.add("c"): 

अब हम संग्रह वर्ग के साथ यह क्रमबद्ध करना चाहते है, तो हम कॉल कर सकते हैं:

Collections.sort(list, String::compareToIgnoreCase); 

लेकिन यह कुछ ऐसा हो सकता है अगर हम कस्टम तुलनित्र को परिभाषित :

Comparator<String> customComp = new MyCustomOrderComparator<>(); 
Collections.sort(list, customComp::compare); 

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

+1

दिलचस्प सवाल बताते हैं शायद और भी स्पष्ट उदाहरण जोड़ लिया है,? सुपर स्ट्रिंग> cmp = स्ट्रिंग :: तुलना करें IgnoreCase; 'ईमानदारी से, मुझे नहीं पता कि यह कैसे काम करता है। – MightyPork

+0

मेरा मानना ​​है कि इसे http://stackoverflow.com/questions/20001427/double-colon-operator-in-java-8 – MightyPork

उत्तर

16

Oracle method references tutorial से: एक विशेष प्रकार

निम्न में से एक मनमाना वस्तु का एक उदाहरण विधि को

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

String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);

विधि संदर्भ String::compareToIgnoreCase के लिए समकक्ष लैम्ब्डा अभिव्यक्ति औपचारिक पैरामीटर सूची (String a, String b) होगी, जहां ए और बी मनमानी नाम हैं जो इस उदाहरण का बेहतर वर्णन करने के लिए उपयोग किए जाते हैं। विधि संदर्भ विधि a.compareToIgnoreCase(b) का आह्वान करेगा।

लेकिन, :: ऑपरेटर का वास्तव में क्या अर्थ है? खैर, :: ऑपरेटर इस तरह वर्णित किया जा सकता (this SO question से):

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

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

तो, विधि है कि इसका मतलब है कि संदर्भ String::compareToIgnoreCase दूसरी श्रेणी (instanceRef::methodName) के अंतर्गत आता है जिसका अर्थ है कि इसका अनुवाद (a, b) -> a.compareToIgnoreCase(b) पर किया जा सकता है।

मुझे विश्वास है कि निम्नलिखित उदाहरण आगे बताते हैं। Comparator<String> में एक विधि है जो दो String ऑपरेटरों पर संचालित होती है और int लौटाती है। इसे छद्म-वर्णित किया जा सकता है (a, b) ==> return int (जहां ऑपरेंड a और b हैं)। आप इसे इस तरह से उस श्रेणी के अंतर्गत निम्नलिखित फॉल्स के सभी देखते हैं:

// Trad anonymous inner class 
// Operands: o1 and o2. Return value: int 
Comparator<String> cTrad = new Comparator<String>() { 
    @Override 
    public int compare(final String o1, final String o2) { 
     return o1.compareToIgnoreCase(o2); 
    } 
}; 

// Lambda-style 
// Operands: o1 and o2. Return value: int 
Comparator<String> cLambda = (o1, o2) -> o1.compareToIgnoreCase(o2); 

// Method-reference à la bullet #2 above. 
// The invokation can be translated to the two operands and the return value of type int. 
// The first operand is the string instance, the second operand is the method-parameter to 
// to the method compareToIgnoreCase and the return value is obviously an int. This means that it 
// can be translated to "instanceRef::methodName". 
Comparator<String> cMethodRef = String::compareToIgnoreCase; 

This great SO-answer to explains how lambda functions are compiled। उस उत्तर में Jarandinor ब्रायन गोएट्ज़ उत्कृष्ट दस्तावेज़ से निम्नलिखित मार्ग को संदर्भित करता है जो more about lambda translations का वर्णन करता है।

इसके बजाय उद्देश्य यह है कि (जैसे एक आंतरिक वर्ग के लिए एक निर्माता बुला के रूप में) लैम्ब्डा अभिव्यक्ति लागू करता है बनाने के लिए बाईटकोड पैदा करने की

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

मूल रूप से इसका अर्थ यह है कि मूल रनटाइम तय करता है कि लैम्ब्डा का अनुवाद कैसे करें।

ब्रायन जारी है:

विधि संदर्भ, लैम्ब्डा अभिव्यक्ति के रूप में एक ही तरह से व्यवहार कर रहे हैं, सिवाय इसके कि सबसे विधि संदर्भ एक नई पद्धति में desugared किए जाने की जरूरत नहीं है, हम संदर्भित विधि के लिए बस एक स्थिर विधि हैंडल लोड कर सकते हैं और मेटाफैक्टरी को पास कर सकते हैं।

तो, lambdas एक नई विधि में desugared हैं। जैसे

class A { 
    public void foo() { 
     List<String> list = ... 
     list.forEach([lambda for lambda$1 as Consumer]); 
    } 

    static void lambda$1(String s) { 
     System.out.println(s); 
    } 
} 

लेकिन, ब्रायन इस दस्तावेज़ में बताते हैं::

class A { 
    public void foo() { 
     List<String> list = ... 
     list.forEach(s -> { System.out.println(s); }); 
    } 
} 

कोड ऊपर कुछ इस तरह के desugared हो जाएगा

अगर desugared विधि एक उदाहरण है विधि, रिसीवर को पहला तर्क माना जाता है

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

तो, this entry by Moandji Ezana की मदद से, एक Comparator<String> रूप compareToIgnoreCase की desugaring नीचे निम्न चरणों का पालन करने के लिए तोड़ा जा सकता है:

  • एक List<String> के लिए Collections#sort उम्मीद एक Comparator<String>
  • Comparator<String> के साथ एक कार्यात्मक इंटरफ़ेस है विधि int sort(String, String), जो BiFunction<String, String, Integer>
  • के बराबर है तो तुलनित्र उदाहरणद्वारा प्रदान किया जा सकता हैसंगत लैम्ब्डा: (String a, String b) -> a.compareToIgnoreCase(b)
  • String::compareToIgnoreCase एक उदाहरण विधि है कि एक String तर्क कहलाता है, तो यह ऊपर लैम्ब्डा साथ संगत है: String a रिसीवर हो जाता है और String b विधि तर्क

संपादित हो जाता है: `तुलनाकारी <: ओपी से इनपुट के बाद मैं एक निम्न स्तर उदाहरण है कि desugaring

+0

के डुप्लिकेट के रूप में बंद किया जा सकता है धन्यवाद, आपने जो कुछ वर्णित किया है वह सच है, लेकिन यह अभी भी बहुत है ओरेकल के ट्यूटोरियल से उच्च स्तर की सोच और उदाहरण समझना आसान है। समस्या अभी भी तुलना के साथ है विधि के लिए जो तुलनाकर्ता की 'तुलना()' विधि हस्ताक्षर से मेल नहीं खाती है। अगर हमारे पास '(ए, बी) -> a.compareTo (बी)' और '(ए, बी) -> comparator.compare (ए, बी)' एक बहुत बड़ा अंतर है। क्या इसका मतलब यह है कि विधि संदर्भ को लैम्ब्डा अभिव्यक्ति हस्ताक्षर '(ए, बी) -> int' से मेल खाना है, और वास्तविक विधि हस्ताक्षर नहीं है? – swch

+1

@ स्लेवेकव। मैंने 'तुलना करने के लिए' स्तर को कम करने "की कोशिश की है कि 'तुलना करने के लिए कैसे अनदेखा करें' * * कंप्रेसर ' की तुलना में * desugared * है। – wassgren

+0

ओह यह एक महान स्पष्टीकरण है, धन्यवाद – swch

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