2015-09-08 7 views
5

मैं अज्ञात वर्गों से निपटने का सामना करने वाले एक अजीब व्यवहार को समझना चाहता हूं।जावा: अज्ञात वर्गों की शुरुआत और लागतकर्ता

मैं एक वर्ग है कि इसके निर्माता के अंदर एक संरक्षित प्रणाली को बुलाती है (मुझे पता है, गरीब डिजाइन, लेकिन यह एक और कहानी है ...)

public class A { 
    public A() { 
    init(); 
    } 
    protected void init() {} 
} 

तो मैं एक और वर्ग है कि A प्रदान करता है और init() को ओवरराइड करता है है।

public class B extends A { 
    int value; 
    public B(int i) { 
    value = i; 
    } 
    protected void init() { 
    System.out.println("value="+value); 
    } 
} 

अगर मैं कोड

B b = new B(10); 

मैं

> value=0 

हो और उसकी वजह सुपर वर्ग के निर्माता B ctor से पहले शुरू हो जाती है और उसके बाद value अब भी है उम्मीद है।

लेकिन जब इस

class C { 
    public static void main (String[] args) { 
    final int avalue = Integer.parsetInt(args[0]); 
    A a = new A() { 
     void init() { System.out.println("value="+avalue); } 
    } 
    } 
} 

की तरह एक गुमनाम वर्ग का उपयोग कर रहा value=0 प्राप्त करने के लिए, क्योंकि यह कम या ज्यादा वर्ग B बराबर होना चाहिए उम्मीद करेंगे: संकलक स्वचालित रूप से एक नया वर्ग C$1 कि A प्रदान करता है और बनाता है बनाता है उदाहरण चर गुमनाम वर्ग के तरीकों में संदर्भित स्थानीय चर स्टोर करने के लिए, एक बंद आदि का अनुकरण ...

लेकिन जब आप इस चलाने के लिए, मैं

मिला 210
> java -cp . C 42 
> value=42 

प्रारंभ में मैं सोच रहा था कि यह जावा 8 का उपयोग कर रहा था, और शायद, लैमडब्स पेश करते समय, वे हुड के तहत अज्ञात वर्गों को लागू करने के तरीके को बदल दिया (अब आपको final की आवश्यकता नहीं है) लेकिन मैं जावा 7 भी साथ की कोशिश की और ...

वास्तव में एक ही परिणाम मिला, javap साथ बाइट कोड को देखते हुए, मैं देख सकता हूँ B है कि

> javap -c B 
Compiled from "B.java" 
public class B extends A { 
    int value; 

    public B(int); 
    Code: 
     0: aload_0 
     1: invokespecial #1     // Method A."<init>":()V 
     4: aload_0 
     5: iload_1 
     6: putfield  #2     // Field value:I 
     9: return 
... 

जबकि C$1 के लिए:

+०१२३५१६४१०
> javap -c C\$1 
Compiled from "C.java" 
final class C$1 extends A { 
    final int val$v; 

    C$1(int); 
    Code: 
     0: aload_0 
     1: iload_1 
     2: putfield  #1     // Field val$v:I 
     5: aload_0 
     6: invokespecial #2     // Method A."<init>":()V 
     9: return 
.... 

क्या कोई मुझे बता सकता है कि यह अंतर क्यों? क्या "सामान्य" कक्षाओं का उपयोग करके अज्ञात वर्ग के व्यवहार को दोहराने का कोई तरीका है?

संपादित करें: प्रश्न को स्पष्ट करने के लिए: अनाम वर्गों का प्रारंभिकरण किसी भी अन्य वर्ग (जहां सुपर कन्स्ट्रक्टर किसी अन्य चर को सेट करने से पहले बुलाया जाता है) शुरू करने के नियमों को तोड़ता है? या, क्या सुपर कन्स्ट्रक्टर inovking से पहले B कक्षा में आवृत्ति चर सेट करने का कोई तरीका है?

+0

आपको क्यों लगता है कि आपका पहला और दूसरा कोड समान है? दूसरे कोड में, आप स्थानीय चर का उपयोग कर रहे हैं। आपके अज्ञात वर्ग विवरण को निष्पादित करने से पहले इसे आरंभ किया जाएगा। –

+0

उम ... ठीक है, आप कहते हैं: तथ्य यह है कि संकलक इस परिदृश्य को लागू करने के लिए एक वर्ग बनाता है डेवलपर को छुपाया जाना चाहिए, इसलिए 'सी $ 1' कक्षा एक विशेष मामला है, और यह ठीक है अगर यह मानक का पालन नहीं करता है कन्स्ट्रक्टर नियम। यह काफी उचित है, लेकिन फिर भी, imho यह थोड़ा अजीब है। – ugo

उत्तर

3

यह प्रश्न सभी आंतरिक कक्षाओं पर लागू होता है, न कि सिर्फ एनन कक्षाओं में। (एनन कक्षाएं आंतरिक कक्षाएं हैं)

जेएलएस यह निर्देश नहीं देता है कि एक आंतरिक वर्ग निकाय बाहरी स्थानीय चर का उपयोग कैसे करता है; यह केवल specifies है कि स्थानीय चर प्रभावी ढंग से अंतिम होते हैं, और निश्चित रूप से आंतरिक वर्ग निकाय के समक्ष असाइन किए जाते हैं। इसलिए, इसका कारण यह है कि आंतरिक वर्ग को स्थानीय चर के निश्चित रूप से असाइन किए गए मान को देखना चाहिए।

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

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


देखने के झामुमो बिंदु से "... संकलक तुरंत enclosing उदाहरण यह कैसे कभी इच्छाओं का प्रतिनिधित्व कर सकते हैं। जावा प्रोग्रामिंग भाषा के लिए ... के लिए कोई जरूरत नहीं है" , यह विनिर्देशन एक समस्या हो सकती है; यह स्पष्ट नहीं है कि आंतरिक वर्ग में पढ़ने के संबंध में लेखन कैसे किए गए थे। यह मानना ​​उचित है कि, एक सिंथेटिक चर पर एक लेखन किया जाता है, जो new InnerClass() कार्रवाई से पहले (प्रोग्रामिंग क्रम में) है; बाहरी वर्ग बाहरी स्थानीय चर या संलग्न उदाहरण देखने के लिए सिंथेटिक चर को पढ़ता है।


वहाँ "सामान्य" वर्गों का उपयोग गुमनाम वर्ग के व्यवहार को दोहराने के लिए कोई तरीका है?

आप बाहरी-भीतरी वर्ग

public class B0 
{ 
    int value; 
    public B0(int i){ value=i; } 

    public class B extends A 
    { 
     protected void init() 
     { 
      System.out.println("value="+value); 
     } 
    } 
} 

यह इस तरह का प्रयोग किया जाएगा, जो प्रिंट 10

new B0(10).new B(); 

एक सुविधा कारखाने विधि जोड़ा जा सकता है के रूप में "सामान्य" वर्ग की व्यवस्था कर सकते हैं सिंटैक्स कुरूपता को छिपाने के लिए

newB(10); 

public static B0.B newB(int arg){ return new B0(arg).new B(); } 

इसलिए हमने अपनी कक्षा को 2 भागों में विभाजित किया; बाहरी भाग सुपर कन्स्ट्रक्टर से पहले भी निष्पादित किया जाता है। यह कुछ मामलों में उपयोगी है। (another example)


(भीतरी अनाम पहुंच स्थानीय चर संलग्न उदाहरण प्रभावी अंतिम सुपर निर्माता)

+0

+1 क्योंकि आंतरिक वर्ग एक बहुत अच्छी चाल है। हालांकि कारखाने की विधि में एक अज्ञात वर्ग पर्याप्त होगा। – Clashsoft

+0

@Clashsoft - आप सही हैं; लेकिन किसी कारण से नामित सबक्लास की आवश्यकता होती है। – ZhongYu

2

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

आपको गुमनाम वर्ग उदाहरण के साथ पहली टुकड़ा करने के लिए एक समान व्यवहार प्राप्त कर सकते हैं अगर आप गुमनाम कक्षा में एक उदाहरण चर का उपयोग:

class C { 
    public static void main (String[] args) { 
    A a = new A() { 
     int avalue = 10; 
     void init() { System.out.println("value="+avalue); } 
    } 
    } 
} 

यह

value=0 

प्रिंट होगा के बाद से init() है avalue से पहले A के कन्स्ट्रक्टर द्वारा निष्पादित किया गया है।

+0

मुझे पता है कि चर पहले ही शुरू हो चुका है, मेरा सवाल इस तथ्य के बारे में था कि बाइट कोड को देखते हुए, अज्ञात वर्ग का निर्माता अन्य "सामान्य" वर्गों के नियमों का पालन नहीं करता है। क्षमा करें, शायद मैं स्पष्ट नहीं था, मैंने सवाल संपादित किया ... – ugo

+0

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

0

दो उदाहरण संबंधित नहीं हैं।

बी उदाहरण में:

final int avalue = Integer.parsetInt(args[0]); 
A a = new A() { 
    void init() { System.out.println("value="+avalue); } 
} 

मूल्य मुद्रित किया जा रहा:

protected void init() { 
    System.out.println("value="+value); 
} 

मूल्य मुद्रित किया जा रहा बी

के कहने के value क्षेत्र गुमनाम उदाहरण में है main() विधि के स्थानीय चर avalue है।

2

गुमनाम कक्षाओं में चर पर कब्जा (सुपर निर्माता कॉल पहले होना चाहिए सामान्य कंस्ट्रक्टर्स के नियमों को तोड़ने के लिए अनुमति दी है कथन) क्योंकि यह कानून केवल संकलक द्वारा लागू किया जाता है। JVM सुपर कन्स्ट्रक्टर का आह्वान करने से पहले किसी भी बाइटकोड को चलाने की अनुमति देता है, जिसका उपयोग संकलितकर्ता द्वारा किया जाता है (यह अपने नियमों को तोड़ देता है!) अज्ञात वर्गों के लिए।

रूप bayou.io के जवाब में दिखाया गया है आप या तो भीतर की कक्षाओं के साथ व्यवहार की नकल कर सकते हैं, या आप एक स्थिर B कारखाने विधि में एक गुमनाम उपयोग कर सकते हैं:

public class B extends A 
{ 
    public static B create(int value) 
    { 
     return new B() { 
      void init() { System.out.println("value="+value); 
     }; 
    } 
} 

सीमा वास्तव में नहीं बल्कि व्यर्थ है और कुछ स्थितियों में कष्टप्रद हो सकता है:

class A 
{ 
    private int len; 

    public A(String s) 
    { 
     this.len = s.length(); 
    } 
} 

class B extends A 
{ 
    private String complexString; 

    public B(int i, double d) 
    { 
     super(computeComplexString(i, d)); 
     this.complexString = computeComplexString(i, d); 
    } 

    private static String computeComplexString(int i, double d) 
    { 
     // some code that takes a long time 
    } 
} 

इस उदाहरण में, आप, computeComplexString गणना दो बार क्या करना है कोई वा न होने के कारण वाई दोनों इसे सुपर कन्स्ट्रक्टर और पर एक आवृत्ति चर में संग्रहीत करते हैं।

+0

'बी (स्ट्रिंग)' जोड़ने के बारे में, और 'बी (i, डी)' कॉल 'इस (computeComplexString (i, d)) ' – ZhongYu

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