Почему javac позволяет компилировать "new CLS (). New InnerCLS ()"? - PullRequest
1 голос
/ 19 февраля 2020

Рассмотрим следующий фрагмент: (Я сталкивался с этим синтаксисом при проверке некоторых декомпилированных файлов классов, и это минимальное представление)

public class Main {
    public static void main(String[] args) {
        Main.Inner o = new Main().new Inner() {};

        System.out.println("Bye from " + o.getClass());
    }

    class Inner {}
}

Это компилируется и работает нормально (я тестировал кучу JDK). Может кто-нибудь объяснить, как получается эта компиляция и что представляет этот код?

Этот код создает 3 класса:

1. Main - This creates the following code (I removed the irrelevant parts):   
    new Main$1
    dup
    new Main
    dup
    invokespecial Method Main <init> ()V
    dup
    invokevirtual Method java/lang/Object getClass ()Ljava/lang/Class;
    pop
    invokespecial Method Main$1 <init> (LMain;)V

Why is it calling getClass (the result is popped anyway)?

2. Main$Inner - This class looks like as would expect an inner class to look

3. Main$1 - This creates the following class (I removed the irrelevant parts):
    final class Main$1 extends Main$Inner 
    method <init> : (LMain;)V 
    aload_0
    aload_1
    dup
    invokevirtual Method java/lang/Object getClass ()Ljava/lang/Class;
    pop
    invokespecial Method Main$Inner <init> (LMain;)V
    return

Again, why is it calling getClass (the result is popped anyway)?

Кстати, он может быть вложен еще дальше как это:

public class Main {
    public static void main(String[] args) {
        Object o = new Main().new Inner1().new Inner2().new Inner3() {};

        System.out.println("Bye from " + o.getClass());
    }

    class Inner1 {
        class Inner2 {
            class Inner3 {
            }
        }
    }
}

Ответы [ 2 ]

2 голосов
/ 19 февраля 2020

Так работают внутренние (не статичные c) экземпляры классов. Вам нужен экземпляр внешнего класса для создания экземпляра внутреннего класса. Это еще один пример:

var outer = new Outer();
var inner = outer.new Outer.Inner();

См .:

1 голос
/ 19 февраля 2020

Внутренние классы должны быть созданы с указателем this на внешний класс.

Синтаксис outer.new Inner() является одним из способов достижения этого (новый экземпляр Inner будет иметь значение outer в качестве внешней ссылки.

Это эквивалентно с нестатическим c методом в основном классе:

class Main {

    Inner makeInner() {
        /// "this." is implied here, but it is there.
        return new Inner();
    }
    class Inner {
    }
}
...