Что значит `someObject.Новый ' делать в Java?
в Java, я только что узнал, что следующий код является законным:
KnockKnockServer newServer = new KnockKnockServer();
KnockKnockServer.receiver receive = newServer.new receiver(clientSocket);
FYI, receiver - это просто вспомогательный класс со следующей подписью:
public class receiver extends Thread { /* code_inside */ }
Я никогда не видел XYZ.new записи. Как это работает? Есть ли способ кодировать это более условно?
5 ответов:
это способ создать экземпляр нестатического внутреннего класса из внешнего тела содержащего класса, как описано в Oracle docs.
каждый экземпляр внутреннего класса связан с экземпляром его содержащего класса. Когда ты
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() } }но если вы хотите создать экземпляр Bar вне Foo, или связать a новый экземпляр с экземпляром, кроме
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:LTest; 5: aload_0 6: invokespecial #2; //Method java/lang/Object."<init>":()V 9: return }все просто - при вызове конструктора TestInner java передает экземпляр теста в качестве первого аргумента главный:12. Не глядя на то, что TestInner должен иметь конструктор без аргументов. Тестиннер в свою очередь просто сохраняет ссылку на родительский объект, Тест$TestInner: 2. Когда вы вызываете конструктор внутреннего класса из метода экземпляра, ссылка на родительский объект передается автоматически, поэтому вам не нужно его указывать. На самом деле его проходит каждый раз, но при вызове извне он должен быть передан явно.
t.new TestInner();- это просто способ указать первый скрытый аргумент для конструктора TestInner, а не типметод() равна к:
public TestInner method(){ return this.new TestInner(); }TestInner равен:
class TestInner{ private Test this; TestInner(Test parent){ this.this = parent; } }
когда внутренние классы были добавлены в Java в версии 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; Foo$Bar(Foo outerThis) { this.this = outerThis; } public void printVal() { System.out.println(this.val); } }имя внутреннего класса имеет префикс с именем внешнего класса, чтобы сделать его уникальным. Скрытый рядовой
thisдобавляется член, который содержит копию внешнего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член со значениемthisСcreateBar(т. е.b.this = f).наконец-то мы называем
b.printVal()который пытается напечататьb.this.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член f. Итак, еще раз,b.this = f.и снова, когда вы называете
b.printVal(), вы печатаетеb.thi.valчтоf.valчто 5.главное помнить, что внутренний класс имеет скрытый член, содержащий копию
thisиз внешнего класса. Когда вы создаете экземпляр внутреннего класса из внешнего класса, он неявно инициализируется текущим значениемthis. Когда вы создаете экземпляр внутреннего класса из внешнего класса, вы явно указываете, какой экземпляр внешнего класса использовать, через префикс наnewключевое слово.
думать
new receiverкак один токен. Вроде как имя функции с пробелом в нем.конечно, класс
KnockKnockServerбуквально не имеет функции с именемnew receiver, но я предполагаю, что синтаксис означает, что. Это должно выглядеть так, как будто вы вызываете функцию, которая создает новый экземплярKnockKnockServer.receiverиспользуя конкретный экземплярKnockKnockServerдля любых обращений к вложенному классу.
слежка
если объявление типа (например, переменной-члена или имени параметра) в определенной области (например, внутренний класс или определение метода) имеет то же имя, что и другое объявление в охватывающей области, то объявление затеняет объявление охватывающей области. Вы не можете ссылаться на затененное объявление только по его имени. Следующий пример, 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 = 0System.out.println("this.x = " + this.x);обратитесь к переменным-членам, которые заключают большие области по имени класса, к которому они принадлежат. Например, следующая инструкция обращается к переменной-члену класса ShadowTest из метода methodInFirstLevel:
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
Comments