자바에서`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
}
Everything is simple - when invoking TestInner constructor, java passes Test instance as a first argument main:12. Not looking at that TestInner should have a no argument constructor. TestInner in its turn just saves reference to parent object, Test$TestInner:2. When you are invoking inner class constructor from an instance method, reference to parent object is passes automatically, so you do not have to specify it. Actually its passes every time, but when invoking from outside it should be passed explicitly.
t.new TestInner();
- is just a way to specify the first hidden argument to TestInner constructor, not a type
method() is equal to:
public TestInner method(){
return this.new TestInner();
}
TestInner is equal to:
class TestInner{
private Test this$0;
TestInner(Test parent){
this.this$0 = parent;
}
}
When inner classes were added to Java in version 1.1 of the language they were originally defined as a transformation to 1.0 compatible code. If you look at an example of this transformation, I think it will make it a lot clearer how an inner class actually works.
Consider the code from Ian Roberts' answer:
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();
}
}
When transformed to 1.0 compatible code, that inner class Bar
would become something like this:
class Foo$Bar {
private Foo this$0;
Foo$Bar(Foo outerThis) {
this.this$0 = outerThis;
}
public void printVal() {
System.out.println(this$0.val);
}
}
The inner class name is prefixed with the outer class name so as to make it unique. A hidden private this$0
member is added that holds a copy of the outer this
. And a hidden constructor is created to initialise that member.
And if you look at the createBar
method, it would be transformed into something like this:
public Foo$Bar createBar() {
return new Foo$Bar(this);
}
So let's see what happens when you execute the following code.
Foo f = new Foo(5);
Foo.Bar b = f.createBar();
b.printVal();
First we instantiate an instance of Foo
and intialise the val
member to 5 (i.e. f.val = 5
).
Next we call f.createBar()
, which instantiates an instance of Foo$Bar
and initialises the this$0
member to the value of this
passed in from createBar
(i.e. b.this$0 = f
).
Finally we call b.printVal()
which tries to print b.this$0.val
which is f.val
which is 5.
Now that was a regular instantiation of an inner class. Let's look at what happens when instantiating Bar
from outside Foo
.
Foo f = new Foo(5);
Foo.Bar b = f.new Bar();
b.printVal();
Applying our 1.0 transformation again, that second line would become something like this:
Foo$Bar b = new Foo$Bar(f);
This is almost identical to the f.createBar()
call. Again we're instantiating an instance of Foo$Bar
and initialising the this$0
member to f. So again, b.this$0 = f
.
And again when you call b.printVal()
, you are printing b.thi$0.val
which is f.val
which is 5.
The key thing to remember is that the inner class has a hidden member holding a copy of this
from the outer class. When you instantiate an inner class from within the outer class, it it implicitly initialised with the current value of this
. When you instantiate the inner class from outside the outer class, you explicitly specify which instance of the outer class to use, via the prefix on the new
keyword.
Think of new receiver
as a single token. Kind of like a function name with a space in it.
Of course, the class KnockKnockServer
does not literally have a function named new receiver
, but I'm guessing the syntax is meant to suggest that. It's meant to look like you're calling a function that creates a new instance of KnockKnockServer.receiver
using a particular instance of KnockKnockServer
for any accesses to the enclosing class.
Shadowing
If a declaration of a type (such as a member variable or a parameter name) in a particular scope (such as an inner class or a method definition) has the same name as another declaration in the enclosing scope, then the declaration shadows the declaration of the enclosing scope. You cannot refer to a shadowed declaration by its name alone. The following example, ShadowTest, demonstrates this:
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);
}
}
The following is the output of this example:
x = 23
this.x = 1
ShadowTest.this.x = 0
This example defines three variables named x: The member variable of the class ShadowTest, the member variable of the inner class FirstLevel, and the parameter in the method methodInFirstLevel. The variable x defined as a parameter of the method methodInFirstLevel shadows the variable of the inner class FirstLevel. Consequently, when you use the variable x in the method methodInFirstLevel, it refers to the method parameter. To refer to the member variable of the inner class FirstLevel, use the keyword this to represent the enclosing scope:
System.out.println("this.x = " + this.x);
Refer to member variables that enclose larger scopes by the class name to which they belong. For example, the following statement accesses the member variable of the class ShadowTest from the method methodInFirstLevel:
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
참고URL : https://stackoverflow.com/questions/15724676/what-does-someobject-new-do-in-java
'developer tip' 카테고리의 다른 글
Chrome의 AJAX가 GET / POST / PUT / DELETE 대신 옵션을 전송합니까? (0) | 2020.08.19 |
---|---|
Google 글꼴의 스타일 및 두께 지정 (0) | 2020.08.19 |
기본 HTTP 및 Bearer 토큰 인증 (0) | 2020.08.18 |
Android 레이아웃 XML에서 background, backgroundTint, backgroundTintMode 속성의 차이점은 무엇입니까? (0) | 2020.08.18 |
LR, SLR 및 LALR 파서의 차이점은 무엇입니까? (0) | 2020.08.18 |