[java] 자바에서`someObject.new`는 무엇을합니까?

Java에서 방금 다음 코드가 합법적이라는 것을 알았습니다.

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

참고로 수신자는 다음 서명이있는 도우미 클래스 일뿐입니다.

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

나는 XYZ.new전에 표기법을 본 적이 없습니다 . 어떻게 작동합니까? 더 일반적으로 코딩하는 방법이 있습니까?



답변

Oracle 문서에 설명 된대로 포함하는 클래스 본문 외부에서 비 정적 내부 클래스를 인스턴스화하는 방법 입니다.

모든 내부 클래스 인스턴스는 포함하는 클래스의 인스턴스와 연결됩니다. 때 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()
  }
}

그러나 Foo 외부에서 Bar의 인스턴스를 만들거나 새 인스턴스를 포함하는 인스턴스가 아닌 다른 인스턴스와 연결 this하려면 접두사 표기법을 사용해야합니다.

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


답변

이 예를 살펴보십시오.

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

}

모든 것이 간단합니다. TestInner 생성자를 호출 할 때 Java는 Test 인스턴스를 첫 번째 인수 main : 12로 전달 합니다. TestInner에는 인수가없는 생성자가 있어야합니다. TestInner는 차례로 부모 개체 인 Test $ TestInner : 2에 대한 참조를 저장합니다 . 인스턴스 메서드에서 내부 클래스 생성자를 호출 할 때 부모 개체에 대한 참조가 자동으로 전달되므로 지정할 필요가 없습니다. 실제로 매번 통과하지만 외부에서 호출 할 때는 명시 적으로 전달해야합니다.

t.new TestInner(); -유형이 아닌 TestInner 생성자에 대한 첫 번째 숨겨진 인수를 지정하는 방법입니다.

method () 는 다음과 같습니다.

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

TestInner 는 다음과 같습니다.

class TestInner{
    private Test this$0;

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


답변

내부 클래스가 언어 버전 1.1에서 Java에 추가되었을 때 원래는 1.0 호환 코드로의 변환으로 정의되었습니다. 이 변환의 예를 살펴보면 내부 클래스가 실제로 작동하는 방식이 훨씬 더 명확해질 것이라고 생각합니다.

Ian Roberts의 답변 코드를 고려하십시오.

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하고 val멤버를 5 (즉 f.val = 5) 로 초기화합니다 .

다음으로를 호출 f.createBar()하여의 인스턴스를 인스턴스화 Foo$Bar하고 this$0멤버를 this전달 된 값 createBar(예 :)으로 초기화합니다 b.this$0 = f.

마지막으로 우리는 전화를 b.printVal()인쇄 할 수있는 b.this$0.valf.val5이다.

이제 그것은 내부 클래스의 정기적 인 인스턴스화였습니다. 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멤버를 f로 초기화합니다 . 다시 b.this$0 = f.

전화 할 때 그리고 다시 b.printVal(), 당신은 인쇄 b.thi$0.val되는 f.val5이다.

기억해야 할 핵심 사항은 내부 클래스 this에 외부 클래스 의 복사본을 보유하는 숨겨진 멤버가 있다는 것 입니다. 외부 클래스 내에서 내부 클래스를 인스턴스화하면 현재 값으로 암시 적으로 초기화됩니다 this. 외부 클래스 외부에서 내부 클래스를 인스턴스화 할 때 new키워드 의 접두사를 통해 사용할 외부 클래스의 인스턴스를 명시 적으로 지정 합니다.


답변

new receiver하나의 토큰으로 생각하십시오 . 공백이있는 함수 이름과 같습니다.

물론 클래스 KnockKnockServer에는 문자 그대로라는 함수가 new receiver없지만 구문이이를 제안하기위한 것으로 추측됩니다. 이는 둘러싸는 클래스 KnockKnockServer.receiverKnockKnockServer대한 모든 액세스를 위해 의 특정 인스턴스를 사용하는 새 인스턴스를 만드는 함수를 호출하는 것처럼 보입니다 .


답변

섀도 잉

특정 범위 (예 : 내부 클래스 또는 메서드 정의)의 형식 선언 (예 : 멤버 변수 또는 매개 변수 이름)이 바깥 쪽 범위의 다른 선언과 동일한 이름을 갖는 경우 선언은 선언을 섀도 잉합니다. 둘러싸는 범위의. 이름만으로 그림자가있는 선언을 참조 할 수 없습니다. 다음 예제 인 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라는 세 개의 변수를 정의합니다. ShadowTest 클래스의 멤버 변수, FirstLevel 내부 클래스의 멤버 변수 및 methodInFirstLevel 메서드의 매개 변수입니다. methodInFirstLevel 메서드의 매개 변수로 정의 된 변수 x는 내부 클래스 FirstLevel의 변수를 숨 깁니다. 따라서 methodInFirstLevel 메서드에서 변수 x를 사용하면 메서드 매개 변수를 참조합니다. 내부 클래스 FirstLevel의 멤버 변수를 참조하려면 this 키워드를 사용하여 둘러싸는 범위를 나타냅니다.

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

더 큰 범위를 속한 클래스 이름으로 묶는 멤버 변수를 참조하십시오. 예를 들어 다음 문은 methodInFirstLevel 메서드에서 ShadowTest 클래스의 멤버 변수에 액세스합니다.

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

문서를 참조하십시오


답변