2017-07-29 7 views
7

मैं कार्यात्मक इंटरफ़ेस के कार्यान्वयन बनाने गया था, नीचे मेरी कोडमेमरी कैसे लैम्ब्डा को असाइन करता है | यह कैसे संदर्भित करता गैर सुपर वर्ग संदर्भ चर द्वारा,

Consumer<Integer> consumer=new Consumer<Integer>() { 
    @Override 
    public void accept(Integer t) { 
     System.out.println(t); 
    } 
}; 

के अनुसार javadoc

एक वर्ग प्रकार टी के एक चर कर सकते है एक अशक्त संदर्भ या वर्ग टी की या किसी भी वर्ग का एक उदाहरण के लिए एक संदर्भ

टी

का एक उपवर्ग यहाँ गुमनाम की वस्तु है कि पकड़ ऑब्जेक्ट बनाया गया, जो Consumer का उप-वर्ग है और संदर्भ चर consumer द्वारा संदर्भित किया जा सकता है, जो ठीक है।

लेकिन मैंने देखा ConsumerFunctionalInterface है, इसलिए मैं भी java8-

में कुछ इस तरह कर सकते हैं लैम्ब्डा

Consumer<Integer> consumer=t->System.out.println(t); 

या 'का उपयोग विधि संदर्भ

Consumer<Integer> consumer=System.out::println; 

का उपयोग करना मुझे कोई उप वर्ग या बेनामी सीएल नहीं जानता है गधे दोनों ऊपर cases.So में बनाया जा रहा यह मुझे दो confusion- परिणाम

1: यहाँ तो consumer उपवर्ग करने के लिए या Consumer की बेनामी वर्ग है, तो यह ऊपर उल्लेख किया अवधारणा चर का उल्लंघन नहीं है जिक्र नहीं है केवल कर सकते हैं बच्चे/स्वयं या null देखें?

2: कैसे स्मृति को लैमडास को असाइन किया जाता है और जेवीएम रन टाइम पर कैसा संभालता है?

+1

वहां * कक्षाएं हैं। यह धुआं और दर्पण है। – user2864740

+0

ऐसा इसलिए है क्योंकि JVM लैम्बडा अभिव्यक्ति/विधि संदर्भ अभिव्यक्ति के पुल विधियों के लिए स्मृति में 'उपभोक्ता' का उप-वर्ग बनायेगा। –

+0

@ होली-जावा मुझे किसी भी '.class' फ़ाइल को क्लास पथ से संबंधित नहीं मिला है। क्या आप कृपया संक्षिप्त कर सकते हैं। स्मृति में – TheCurious

उत्तर

4

आप SE8 15.27 का उल्लेख करने की जरूरत है:

15.27.3। एक लैम्ब्डा अभिव्यक्ति

एक लैम्ब्डा अभिव्यक्ति एक काम संदर्भ में संगत है, मंगलाचरण संदर्भ, या एक लक्ष्य प्रकार टी के साथ संदर्भ कास्टिंग करता है, तो टी एक कार्यात्मक इंटरफ़ेस प्रकार (§9.8) और प्रकार अभिव्यक्ति के साथ अनुकूल है जमीनी लक्ष्य टी

क्रम से प्राप्त प्रकार invokedynamic के साथ प्रयोग के मुश्किल चीजों से निपटने में समारोह प्रकार। चलो कुछ कोड की जांच:

import java.util.function.*; 

class R implements Runnable { 
    public void run() { System.out.println("there"); } 
} 

public class L { 
    public static void execute(Runnable r) { 
    System.out.println(r.getClass()); 
    r.run(); 
    } 
    public static void main(String []a) { 
    execute(new R()); // subclass 
    execute(new Runnable() { // anonymous subclass 
     public void run() { System.out.println("elsewhere"); } 
     }); 
    execute(() -> System.out.println("here")); // lambda 
    } 
} 

निष्पादन देता है:

> java L 
class R 
there 
class L$1 
elsewhere 
class L$$Lambda$1/791452441 
here 

पहले दो के लिए वहाँ (दिए गए क्रम में) कोई आश्चर्य की बात, वस्तु विधि execute द्वारा प्राप्त है के वर्ग है R (Runnable का उप प्रकार), L$1 (Runnable का अज्ञात उप प्रकार, और L$$Lambda$1/791452441 (Runnable का उप प्रकार लैम्ब्डा से रन टाइम पर बनाया गया)।ध्यान दें कि लैम्ब्डा के मामले में .class फ़ाइल नहीं है, प्रकार विशेष निर्माण द्वारा रनटाइम पर बनाया गया है। के बाईटकोड जांच:

> javap -c -v L 
Classfile /private/tmp/L.class 
    Last modified 1 août 2017; size 1234 bytes 
    MD5 checksum 9680a2bc143d25344979bae00fff3db7 
    Compiled from "L.java" 
public class L 
    minor version: 0 
    major version: 52 
    flags: ACC_PUBLIC, ACC_SUPER 
Constant pool: 
    #1 = Methodref   #15.#28  // java/lang/Object."<init>":()V 
    #2 = Fieldref   #29.#30  // java/lang/System.out:Ljava/io/PrintStream; 
    #3 = Methodref   #15.#31  // java/lang/Object.getClass:()Ljava/lang/Class; 
    #4 = Methodref   #32.#33  // java/io/PrintStream.println:(Ljava/lang/Object;)V 
    #5 = InterfaceMethodref #34.#35  // java/lang/Runnable.run:()V 
    #6 = Class    #36   // R 
    #7 = Methodref   #6.#28   // R."<init>":()V 
    #8 = Methodref   #14.#37  // L.execute:(Ljava/lang/Runnable;)V 
    #9 = Class    #38   // L$1 
    #10 = Methodref   #9.#28   // L$1."<init>":()V 
    #11 = InvokeDynamic  #0:#43   // #0:run:()Ljava/lang/Runnable; 
    #12 = String    #44   // here 
    #13 = Methodref   #32.#45  // java/io/PrintStream.println:(Ljava/lang/String;)V 
    #14 = Class    #46   // L 
    #15 = Class    #47   // java/lang/Object 
    #16 = Utf8    InnerClasses 
    #17 = Utf8    <init> 
    #18 = Utf8    ()V 
    #19 = Utf8    Code 
    #20 = Utf8    LineNumberTable 
    #21 = Utf8    execute 
    #22 = Utf8    (Ljava/lang/Runnable;)V 
    #23 = Utf8    main 
    #24 = Utf8    ([Ljava/lang/String;)V 
    #25 = Utf8    lambda$main$0 
    #26 = Utf8    SourceFile 
    #27 = Utf8    L.java 
    #28 = NameAndType  #17:#18  // "<init>":()V 
    #29 = Class    #48   // java/lang/System 
    #30 = NameAndType  #49:#50  // out:Ljava/io/PrintStream; 
    #31 = NameAndType  #51:#52  // getClass:()Ljava/lang/Class; 
    #32 = Class    #53   // java/io/PrintStream 
    #33 = NameAndType  #54:#55  // println:(Ljava/lang/Object;)V 
    #34 = Class    #56   // java/lang/Runnable 
    #35 = NameAndType  #57:#18  // run:()V 
    #36 = Utf8    R 
    #37 = NameAndType  #21:#22  // execute:(Ljava/lang/Runnable;)V 
    #38 = Utf8    L$1 
    #39 = Utf8    BootstrapMethods 
    #40 = MethodHandle  #6:#58   // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; 
    #41 = MethodType   #18   // ()V 
    #42 = MethodHandle  #6:#59   // invokestatic L.lambda$main$0:()V 
    #43 = NameAndType  #57:#60  // run:()Ljava/lang/Runnable; 
    #44 = Utf8    here 
    #45 = NameAndType  #54:#61  // println:(Ljava/lang/String;)V 
    #46 = Utf8    L 
    #47 = Utf8    java/lang/Object 
    #48 = Utf8    java/lang/System 
    #49 = Utf8    out 
    #50 = Utf8    Ljava/io/PrintStream; 
    #51 = Utf8    getClass 
    #52 = Utf8    ()Ljava/lang/Class; 
    #53 = Utf8    java/io/PrintStream 
    #54 = Utf8    println 
    #55 = Utf8    (Ljava/lang/Object;)V 
    #56 = Utf8    java/lang/Runnable 
    #57 = Utf8    run 
    #58 = Methodref   #62.#63  // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; 
    #59 = Methodref   #14.#64  // L.lambda$main$0:()V 
    #60 = Utf8    ()Ljava/lang/Runnable; 
    #61 = Utf8    (Ljava/lang/String;)V 
    #62 = Class    #65   // java/lang/invoke/LambdaMetafactory 
    #63 = NameAndType  #66:#69  // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; 
    #64 = NameAndType  #25:#18  // lambda$main$0:()V 
    #65 = Utf8    java/lang/invoke/LambdaMetafactory 
    #66 = Utf8    metafactory 
    #67 = Class    #71   // java/lang/invoke/MethodHandles$Lookup 
    #68 = Utf8    Lookup 
    #69 = Utf8    (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; 
    #70 = Class    #72   // java/lang/invoke/MethodHandles 
    #71 = Utf8    java/lang/invoke/MethodHandles$Lookup 
    #72 = Utf8    java/lang/invoke/MethodHandles 
{ 
    public L(); 
    descriptor:()V 
    flags: ACC_PUBLIC 
    Code: 
     stack=1, locals=1, args_size=1 
     0: aload_0 
     1: invokespecial #1     // Method java/lang/Object."<init>":()V 
     4: return 
     LineNumberTable: 
     line 9: 0 

    public static void execute(java.lang.Runnable); 
    descriptor: (Ljava/lang/Runnable;)V 
    flags: ACC_PUBLIC, ACC_STATIC 
    Code: 
     stack=2, locals=1, args_size=1 
     0: getstatic  #2     // Field java/lang/System.out:Ljava/io/PrintStream; 
     3: aload_0 
     4: invokevirtual #3     // Method java/lang/Object.getClass:()Ljava/lang/Class; 
     7: invokevirtual #4     // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 
     10: aload_0 
     11: invokeinterface #5, 1   // InterfaceMethod java/lang/Runnable.run:()V 
     16: return 
     LineNumberTable: 
     line 11: 0 
     line 12: 10 
     line 13: 16 

    public static void main(java.lang.String[]); 
    descriptor: ([Ljava/lang/String;)V 
    flags: ACC_PUBLIC, ACC_STATIC 
    Code: 
     stack=2, locals=1, args_size=1 
     0: new   #6     // class R 
     3: dup 
     4: invokespecial #7     // Method R."<init>":()V 
     7: invokestatic #8     // Method execute:(Ljava/lang/Runnable;)V 
     10: new   #9     // class L$1 
     13: dup 
     14: invokespecial #10     // Method L$1."<init>":()V 
     17: invokestatic #8     // Method execute:(Ljava/lang/Runnable;)V 
     20: invokedynamiC#11, 0    // InvokeDynamiC#0:run:()Ljava/lang/Runnable; 
     25: invokestatic #8     // Method execute:(Ljava/lang/Runnable;)V 
     28: return 
     LineNumberTable: 
     line 15: 0 
     line 16: 10 
     line 19: 20 
     line 20: 28 
} 
SourceFile: "L.java" 
InnerClasses: 
    statiC#9; //class L$1 
    public static final #68= #67 of #70; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles 
BootstrapMethods: 
    0: #40 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; 
    Method arguments: 
     #41()V 
     #42 invokestatic L.lambda$main$0:()V 
     #41()V 

पहले दिलचस्प हिस्सा main का कोड है:

public static void main(java.lang.String[]); 
    descriptor: ([Ljava/lang/String;)V 
    flags: ACC_PUBLIC, ACC_STATIC 
    Code: 
     stack=2, locals=1, args_size=1 
     0: new   #6     // class R 
     3: dup 
     4: invokespecial #7     // Method R."<init>":()V 
     7: invokestatic #8     // Method execute:(Ljava/lang/Runnable;)V 
     10: new   #9     // class L$1 
     13: dup 
     14: invokespecial #10     // Method L$1."<init>":()V 
     17: invokestatic #8     // Method execute:(Ljava/lang/Runnable;)V 
     20: invokedynamiC#11, 0    // InvokeDynamiC#0:run:()Ljava/lang/Runnable; 
     25: invokestatic #8     // Method execute:(Ljava/lang/Runnable;)V 
     28: return 

आप देख सकते हैं के रूप में वहाँ इंटरफ़ेस का स्पष्ट कार्यान्वयन या अनाम एक के बीच में कोई अंतर नहीं है। आखिरी में केवल कक्षा नामकरण ट्रिकरी (L$1) शामिल है, लेकिन दोनों का उपयोग उसी तरह किया जाता है, invokestatic के माध्यम से।

दिलचस्प मामला तीसरा (लैम्ब्डा एक) है जिसमें invokedynamic और फिर invokestatic शामिल है। ध्यान दें कि invokestatic दो पिछली कॉल (विधि run) में समान विधि को कॉल करता है।

मोटे तौर पर, पहली बार invokedynamic कहा जाता है एक बूटस्ट्रैप विधि निर्माण करने के लिए कहा जाता है एक CallSite (CallSite in Java API देखें) जो तब लैम्ब्डा का कोड निष्पादित करने के लिए आगे इस्तेमाल किया जाएगा।

BootstrapMethods: 
    0: #40 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; 
    Method arguments: 
     #41()V 
     #42 invokestatic L.lambda$main$0:()V 
     #41()V 

और कोड कॉल साइट से रेफ़र किया: यहां बूटस्ट्रैप कॉल देखें

#41 = MethodType   #18   // ()V 
    #42 = MethodHandle  #6:#59   // invokestatic L.lambda$main$0:()V 
    #43 = NameAndType  #57:#60  // run:()Ljava/lang/Runnable; 
    #44 = Utf8    here 
    #45 = NameAndType  #54:#61  // println:(Ljava/lang/String;)V 
    #46 = Utf8    L 
    #47 = Utf8    java/lang/Object 
    #48 = Utf8    java/lang/System 
    #49 = Utf8    out 
    #50 = Utf8    Ljava/io/PrintStream; 
    #51 = Utf8    getClass 
    #52 = Utf8    ()Ljava/lang/Class; 
    #53 = Utf8    java/io/PrintStream 
    #54 = Utf8    println 
    #55 = Utf8    (Ljava/lang/Object;)V 
    #56 = Utf8    java/lang/Runnable 
    #57 = Utf8    run 
+0

बूटस्ट्रैप विधि लैम्बडा के लिए 'वर्ग' फ़ाइल बनाएगी (मान लें कि एक क्लास जो 'java.util.Predicate'' को बढ़ाती है। फिर आंतरिक रूप से यह 'भविष्यवाणी # टेस्ट' डी-श्वेर्ड लैम्ब्डा अभिव्यक्ति को कॉल करेगा (कक्षा के अंदर एक स्थिर आंतरिक विधि जहां लैम्ब्डा का उपयोग किया जाता है)। यह 'test' विधि 'में' MethodHandle' भी बनाएगा (जो 'कॉलसाइट' ऑब्जेक्ट के भीतर निहित होगा); यह 'कॉलसाइट' को एक बार, आमंत्रण से जोड़ा जाएगा। – Eugene

3

क्यू: उपभोक्ता उपवर्ग या उपभोक्ता की बेनामी वर्ग की बात नहीं कर रहा है, तो यहाँ .... ?

वास्तव में, उप-वर्ग लिंकेज चरण invokedynamc निर्देश द्वारा पेश किया गया है।

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

क्यू: कैसे स्मृति lamdas को असाइन करने और कैसे करता है JVM संभाल ऐसे रन टाइम पर?

यह तीन चरणों से क्रम में आगे बढ़ती:

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

अधिक जानकारी के लिए, आप LambdaMetafactory को और भी देख सकते हैं।

4

सबसे पहले जीन-बैपटिस्ट ने आपको दिखाया है कि असाइनमेंट पहले स्थान पर क्यों काम करता है।

अब जो हिस्सा मुझे लगता है कि आप गायब हैं, यह तथ्य यह है कि के जेनरेटेड वर्ग Consumer<Integer> consumer = t -> System.out.println(t); के मामले में केवल invokedynamic के कारण रनटाइम पर दिखाई देता है।

एक ध्वज के साथ अपने वर्ग चलाएँ:

java -Djdk.internal.lambda.dumpProxyClasses=/Your/Path 

और तुम वहाँ एक उत्पन्न वर्ग (वर्ग के अपने पैकेज का नाम से फ़ोल्डरों के रास्ते अंदर) है कि देखेंगे कि एक .class फ़ाइल प्रकार की तरह होता है यह SOQuestion$$Lambda$1.class

आप डिकंपाइल हैं कि आपको लगता है कि यह वास्तव में एक वर्ग को लागू करने वाली है देखेंगे Consumer:

final class org.eugene.so.SOQuestion$$Lambda$1 
      implements java.util.function.Consumer { 
    public void accept(java.lang.Object); 
} 

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

private static void lambda$main$0(java.lang.Integer); 
Code: 
    0: getstatic  #3 // Field java/lang/System.out:Ljava/io/PrintStream; 
    3: aload_0 
    4: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 
    7: return 

जहाँ तक कैसे VM स्मृति इसके लिए, एक गैर पर कब्जा करने लैम्ब्डा आप एक सिंगलटन मिल जाएगा के लिए, एक कैप्चरिंग-लैम्ब्डा के लिए प्रबंधन करता है के रूप में आप प्रत्येक कॉल का एक नया उदाहरण मिल जाएगा। पूर्ण पढ़ना चाहिए here

+2

अरे, तुम क्या कर रहे हो? आपने इसे और अधिक विस्तृत रूप में समझाया है जो मैंने कहा था, और [@ होल्गर का उत्तर] (https://stackoverflow.com/questions/23983832/is-method-reference-caching-a-good-idea-in-java-8/23 99133 9 # 23 99133 9) ने "* इसमें लिंक चरण * में एक नई कक्षा शामिल हो सकती है" का वर्णन किया है, इसलिए आपके पास +1 है। बहुत बढ़िया, :) –

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