जावा में `someObject.new` क्या करता है?


100

जावा में, मुझे अभी पता चला है कि निम्नलिखित कोड कानूनी है:

KnockKnockServer newServer = new KnockKnockServer();                    
KnockKnockServer.receiver receive = newServer.new receiver(clientSocket);

FYI, रिसीवर निम्नलिखित सहायक के साथ सिर्फ एक सहायक वर्ग है:

public class receiver extends Thread {  /* code_inside */  }

मैंने पहले कभी XYZ.newनोटेशन नहीं देखा । वह कैसे काम करता है? क्या अधिक परंपरागत रूप से कोड करने का कोई तरीका है?


7
आपके संदर्भ के लिए, आंतरिक वर्ग
एल्विन वोंग

1
इसके अलावा, मेरा मानना ​​था कि newबहुत सारी भाषाओं में एक ऑपरेटर है। (मुझे लगा कि आप newC ++ में भी ओवरलोड कर सकते हैं ?) जावा का आंतरिक वर्ग मेरे लिए थोड़ा अजीब है, हालाँकि।
एल्विन वोंग

5
StackOverflow पर कोई मूर्खतापूर्ण सवाल नहीं हैं!
इसहाक राबिनोविच

2
@IsaacRabinovitch - कोई बेवकूफ सवाल नहीं हैं। हालांकि, बहुत सारे मूर्ख लोग हैं। (और एक सामयिक मूर्खतापूर्ण उत्तर के रूप में अच्छी तरह से।)
हॉट लिक्स

2
@HotLicks और मूर्खतापूर्ण सवाल की आपकी परिभाषा क्या है? एक तुम पूछने के लिए बहुत स्मार्ट हो, मुझे लगता है। अच्छा है कि आपके पास इतना आत्म-सम्मान है।
इसहाक रैबिनोविच

जवाबों:


120

यह ओरेकल डॉक्स में वर्णित के रूप में युक्त वर्ग निकाय के बाहर से एक गैर-स्थिर आंतरिक वर्ग को त्वरित करने का तरीका है ।

प्रत्येक आंतरिक वर्ग का उदाहरण इसके युक्त वर्ग के उदाहरण के साथ जुड़ा हुआ है। जब आप newएक आंतरिक वर्ग को उसके युक्त वर्ग के भीतर से thisडिफ़ॉल्ट रूप से कंटेनर के उदाहरण का उपयोग करते हैं :

public class Foo {
  int val;
  public Foo(int v) { val = v; }

  class Bar {
    public void printVal() {
      // this is the val belonging to our containing instance
      System.out.println(val);
    }
  }

  public Bar createBar() {
    return new Bar(); // equivalent of this.new Bar()
  }
}

लेकिन अगर आप फू के बाहर बार का एक उदाहरण बनाना चाहते हैं, या किसी अन्य उदाहरण के साथ एक नई आवृत्ति को जोड़ते हैं thisतो आपको उपसर्ग संकेतन का उपयोग करना होगा।

Foo f = new Foo(5);
Foo.Bar b = f.new Bar();
b.printVal(); // prints 5

18
और, जैसा कि आप बता सकते हैं, यह अविश्वसनीय रूप से भ्रमित हो सकता है। आदर्श रूप से, आंतरिक कक्षाओं को बाहरी वर्ग का कार्यान्वयन विवरण होना चाहिए और बाहरी दुनिया के संपर्क में नहीं आना चाहिए।
एरिक जबलो

10
@ EricJablow वास्तव में, यह सिंटैक्स के उन बिट्स में से एक है, जो उस युक्ति को सुसंगत रखने के लिए मौजूद है, लेकिन 99.9999% समय आपको वास्तव में इसका उपयोग करने की आवश्यकता नहीं है। यदि बाहरी लोगों को वास्तव में बार इंस्टेंसेस बनाने की आवश्यकता होती है, तो मैं उन्हें उपयोग करने के बजाय फू पर एक कारखाना विधि प्रदान करूंगा f.new
इयान रॉबर्ट्स

मुझे सही करें अगर मैं गलत हूं, लेकिन अगर publicपहुंच का स्तर KnockKnockServer.receiverबना दिया गया था, privateतो इस तरह से पलटना असंभव होगा, है ना? @EricJablow की टिप्पणी का विस्तार करने के लिए, आंतरिक कक्षाओं को आम तौर पर privateएक्सेस स्तर तक हमेशा डिफ़ॉल्ट होना चाहिए ।
एंड्रयू बिसेल

1
@AndrewBissell हाँ, लेकिन receiverबाहर से कक्षा को संदर्भित करना भी असंभव होगा । अगर मैं इसे डिजाइन कर रहा होता तो शायद मैं क्लास पब्लिक होता लेकिन इसका कंस्ट्रक्टर संरक्षित या पैकेज-प्राइवेट है, और KnockKnockServerरिसीवर इंस्टेंस बनाने के लिए एक तरीका है ।
इयान रॉबर्ट्स

1
@emory मैं यह नहीं कह रहा हूं , मुझे पता है कि एक आंतरिक वर्ग को सार्वजनिक करने के लिए पूरी तरह से वैध कारण हो सकते हैं, और बाहरी वर्ग के तरीकों से आंतरिक वर्ग के उदाहरणों को वापस कर सकते हैं, लेकिन मैं अपने कोड को इस तरह डिजाइन करना चाहूंगा " बाहरी लोगों को सीधे उपयोग करने वाले आंतरिक वर्ग के उदाहरणों का निर्माण करने की आवश्यकता नहीं है x.new
इयान रॉबर्ट्स

18

इस उदाहरण पर एक नज़र डालें:

public class Test {

    class TestInner{

    }

    public TestInner method(){
        return new TestInner();
    }

    public static void main(String[] args) throws Exception{
        Test t = new Test();
        Test.TestInner ti = t.new TestInner();
    }
}

Javap का उपयोग करके हम इस कोड के लिए उत्पन्न निर्देश देख सकते हैं

मुख्य विधि:

public static void main(java.lang.String[])   throws java.lang.Exception;
  Code:
   0:   new     #2; //class Test
   3:   dup
   4:   invokespecial   #3; //Method "<init>":()V
   7:   astore_1
   8:   new     #4; //class Test$TestInner
   11:  dup
   12:  aload_1
   13:  dup
   14:  invokevirtual   #5; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   17:  pop
   18:  invokespecial   #6; //Method Test$TestInner."<init>":(LTest;)V
   21:  astore_2
   22:  return
}

इनर क्लास कंस्ट्रक्टर:

Test$TestInner(Test);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield        #1; //Field this$0:LTest;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
   9:   return

}

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

t.new TestInner(); - TestInner कंस्ट्रक्टर के लिए पहले छिपे हुए तर्क को निर्दिष्ट करने का एक तरीका है, न कि एक प्रकार

विधि () इसके बराबर है:

public TestInner method(){
    return this.new TestInner();
}

TestInner इसके बराबर है:

class TestInner{
    private Test this$0;

    TestInner(Test parent){
        this.this$0 = parent;
    }
}

7

जब आंतरिक कक्षाओं को जावा में भाषा के संस्करण 1.1 में जोड़ा गया था, तो उन्हें मूल रूप से 1.0 संगत कोड में परिवर्तन के रूप में परिभाषित किया गया था। यदि आप इस परिवर्तन का एक उदाहरण देखते हैं, मुझे लगता है कि यह बहुत स्पष्ट कर देगा कि एक आंतरिक वर्ग वास्तव में कैसे काम करता है।

इयान रॉबर्ट्स के उत्तर के कोड पर विचार करें:

public class Foo {
  int val;
  public Foo(int v) { val = v; }

  class Bar {
    public void printVal() {
      System.out.println(val);
    }
  }

  public Bar createBar() {
    return new Bar();
  }
}

1.0 संगत कोड में तब्दील होने पर, आंतरिक वर्ग Barकुछ इस तरह बन जाएगा:

class Foo$Bar {
  private Foo this$0;

  Foo$Bar(Foo outerThis) {
    this.this$0 = outerThis;
  }

  public void printVal() {
    System.out.println(this$0.val);
  }
}

आंतरिक वर्ग का नाम बाहरी वर्ग के नाम के साथ उपसर्ग किया जाता है ताकि इसे अद्वितीय बनाया जा सके। एक छिपा हुआ निजी this$0सदस्य जोड़ा जाता है जो बाहरी की एक प्रति रखता हैthis । और उस सदस्य को इनिशियलाइज़ करने के लिए एक हिडन कंस्ट्रक्टर बनाया जाता है।

और यदि आप createBarविधि को देखते हैं, तो यह कुछ इस तरह से बदल जाएगा:

public Foo$Bar createBar() {
  return new Foo$Bar(this);
}

तो आइए देखें कि जब आप निम्नलिखित कोड निष्पादित करते हैं तो क्या होता है।

Foo f = new Foo(5);
Foo.Bar b = f.createBar();                               
b.printVal();

पहले हम एक उदाहरण के लिए Fooत्वरित करते हैं और val5 (यानी f.val = 5) के लिए सदस्य को सूचित करते हैं ।

इसके बाद हम कॉल करते हैं f.createBar(), जो इंस्टेंट का उदाहरण देता है Foo$Barऔर this$0सदस्य को (यानी ) thisमें पास किए गए मूल्य को इनिशियलाइज़ करता है ।createBarb.this$0 = f

अंत में हम कॉल करते हैं b.printVal()जो प्रिंट करने की कोशिश करता b.this$0.valहै f.valजो कि 5 है।

अब वह एक आंतरिक वर्ग की नियमित तात्कालिकता थी। आइए देखें कि जब Barबाहर से तात्कालिकता होती है तो क्या होता है Foo

Foo f = new Foo(5);
Foo.Bar b = f.new Bar();
b.printVal();

हमारे 1.0 परिवर्तन को फिर से लागू करते हुए, दूसरी पंक्ति कुछ इस तरह बन जाएगी:

Foo$Bar b = new Foo$Bar(f);

यह f.createBar()कॉल करने के लिए लगभग समान है । एक बार फिर हम एक उदाहरण की तात्कालिकता कर रहे हैं Foo$Barऔर this$0सदस्य को आरम्भ कर रहे हैं । इसलिए फिर से,b.this$0 = f

और फिर जब आप कॉल b.printVal(), आप प्रिंट कर रहे हैं b.thi$0.valजो है f.val, जो 5 है।

याद रखने वाली महत्वपूर्ण बात यह है कि आंतरिक वर्ग में एक छिपा सदस्य thisहोता है, जो बाहरी कक्षा से एक कॉपी रखता है । जब आप बाहरी वर्ग के भीतर से एक आंतरिक वर्ग को त्वरित करते हैं, तो यह वर्तमान मूल्य के साथ प्रारंभिक रूप से आरोपित होता है this। जब आप बाहरी वर्ग के बाहर से आंतरिक वर्ग को त्वरित करते हैं, तो आप स्पष्ट रूप से newकीवर्ड पर उपसर्ग के माध्यम से बाहरी वर्ग के किस उदाहरण का उपयोग करना चाहते हैं ।


4

new receiverएक ही टोकन के रूप में सोचो । इसमें एक स्थान के साथ एक फ़ंक्शन नाम की तरह।

बेशक, कक्षा का KnockKnockServerशाब्दिक रूप से एक फ़ंक्शन नाम नहीं है new receiver, लेकिन मुझे लगता है कि वाक्यविन्यास सुझाव देने के लिए है। यह देखने के लिए है कि आप ऐसे फ़ंक्शन को कॉल कर रहे हैं जो एन्क्लोज़िंग क्लास में किसी भी एक्सेस KnockKnockServer.receiverके KnockKnockServerलिए किसी विशेष इंस्टेंस का उपयोग करने का एक नया उदाहरण बनाता है ।


धन्यवाद, हाँ - यह अब मुझे new receiverएक टोकन के रूप में सोचने में मदद करता है ! बहुत बहुत धन्यवाद!
कॉफी

1

पीछा

यदि किसी विशेष दायरे (जैसे इनर क्लास या मेथड डेफिनिशन) में किसी प्रकार (जैसे कि एक सदस्य चर या एक पैरामीटर नाम) की घोषणा होती है, तो एनक्लोजिंग स्कोप में एक और घोषणा के रूप में एक ही नाम होता है, तो घोषणापत्र घोषणा को छाया देता है। घेरने की गुंजाइश। आप केवल उसके नाम से एक छायांकित घोषणा का उल्लेख नहीं कर सकते। निम्न उदाहरण, ShadowTest, यह प्रदर्शित करता है:

public class ShadowTest {

    public int x = 0;

    class FirstLevel {

        public int x = 1;

        void methodInFirstLevel(int x) {
            System.out.println("x = " + x);
            System.out.println("this.x = " + this.x);
            System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
        }
    }

    public static void main(String... args) {
        ShadowTest st = new ShadowTest();
        ShadowTest.FirstLevel fl = st.new FirstLevel();
        fl.methodInFirstLevel(23);
    }
}

इस उदाहरण का आउटपुट निम्न है:

x = 23
this.x = 1
ShadowTest.this.x = 0

यह उदाहरण x नाम के तीन चर को परिभाषित करता है: वर्ग शैडोटेस्ट का सदस्य चर, आंतरिक वर्ग FirstLevel का सदस्य चर, और विधि मे विधि InFirstLevel। चर x को विधि मेथड के पैरामीटर के रूप में परिभाषित किया गया है ।FirstLevel आंतरिक वर्ग FirstLevel के चर को छाया देता है। नतीजतन, जब आप मेथड x का उपयोग मेथड मे करते हैं। InFirstLevel, यह मेथड पैरामीटर को संदर्भित करता है। आंतरिक वर्ग FirstLevel के सदस्य चर को संदर्भित करने के लिए, इस शब्दकूट का उपयोग संलग्नक क्षेत्र को दर्शाने के लिए करें:

System.out.println("this.x = " + this.x);

सदस्य चर का संदर्भ लें जो वर्ग के नाम से बड़े स्कोप को संलग्न करते हैं जिससे वे संबंधित हैं। उदाहरण के लिए, निम्न कथन विधि विधि से वर्ग ShadowTest के सदस्य चर तक पहुँचता है ।FirstLevel:

System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);

डॉक्स का संदर्भ लें

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