Что значит `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 записи. Как это работает? Есть ли способ кодировать это более условно?

281   5  

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 = 0
System.out.println("this.x = " + this.x);

обратитесь к переменным-членам, которые заключают большие области по имени класса, к которому они принадлежат. Например, следующая инструкция обращается к переменной-члену класса ShadowTest из метода methodInFirstLevel:

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

обратитесь к документации

Comments

    Ничего не найдено.