Почему анонимный внутренний класс не содержит ничего сгенерированного из этого кода?

package com.test;

public class OuterClass {
    public class InnerClass {
        public class InnerInnerClass {

        }
    }

    public class InnerClass2 {

    }

    //this class should not exist in OuterClass after dummifying
    private class PrivateInnerClass {
        private String getString() {
            return "hello PrivateInnerClass";
        }
    }

    public String getStringFromPrivateInner() {
        return new PrivateInnerClass().getString();
    }
}

При пробежке javac на командной строке с Sun JVM 1.6.0_20, этот код производит 6 .class файлов:

OuterClass.class
OuterClass$1.class
OuterClass$InnerClass.class
OuterClass$InnerClass2.class
OuterClass$InnerClass$InnerInnerClass.class
OuterClass$PrivateInnerClass.class

При пробежке JDT в затмении он производит только 5 классов.

OuterClass.class
OuterClass$1.class
OuterClass$InnerClass.class
OuterClass$InnerClass2.class
OuterClass$InnerClass$InnerInnerClass.class
OuterClass$PrivateInnerClass.class

При декомпиляции, OuterClass$1.class ничего не содержит. Куда этот дополнительный класс прибывает из и почему он создается?

33
задан Fund Monica's Lawsuit 31 March 2015 в 13:17
поделиться

4 ответа

У меня нет ответа, но я могу подтвердить это и сократить фрагмент до следующего:

public class OuterClass {
    private class PrivateInnerClass {
    }
    public void instantiate() {
        new PrivateInnerClass();
    }
}

Это создает OuterClass$1.class

Compiled from "OuterClass.java"
class OuterClass$1 extends java.lang.Object{
}

И вот javap -c для OuterClass. class:

Compiled from "OuterClass.java"
public class OuterClass extends java.lang.Object{
public OuterClass();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public void instantiate();
  Code:
   0:   new     #2; //class OuterClass$PrivateInnerClass
   3:   dup
   4:   aload_0
   5:   aconst_null
   6:   invokespecial #3; //Method OuterClass$PrivateInnerClass."<init>":
                          //(LOuterClass;LOuterClass$1;)V
   9:   pop
   10:  return

}

И для OuterClass$PrivateInnerClass:

Compiled from "OuterClass.java"
class OuterClass$PrivateInnerClass extends java.lang.Object{
final OuterClass this$0;

OuterClass$PrivateInnerClass(OuterClass, OuterClass$1);
  Code:
   0:   aload_0
   1:   aload_1
   2:   invokespecial   #1; //Method "<init>":(LOuterClass;)V
   5:   return

}

Как видите, синтезированный конструктор принимает аргумент OuterClass$1.

Таким образом, javac создает конструктор по умолчанию, принимающий дополнительный аргумент типа $1, и значение этого аргумента по умолчанию равно 5: aconst_null.


Я обнаружил, что $1 не создается, если верно одно из следующих условий:

  • Вы делаете public class PrivateInnerClass
  • Вы объявляете нулевой конструктор для PrivateInnerClass
  • Или вы не вызываете new на нем
  • Возможно, другие вещи (например. например, static вложенные, и т. д.).

Возможно, связано

  • ID ошибки:4295934: Компиляция частного внутреннего класса создает анонимный файл класса в неправильном каталоге

Создайте следующий исходный текст в каталоге с именем test:

package test;
public class testClass
{
 private class Inner
 {
 }
 public testClass()
 {
 Inner in = new Inner();
 }
}

Скомпилируйте файл из родительского каталога javac test/testClass.java

Обратите внимание, что в текущем каталоге создан файл testClass$1.class. Не уверен, зачем вообще создается этот файл, поскольку также создан test/testClass$Inner.class.

ОЦЕНКА

Файл testClass$1.class предназначен для фиктивного класса, необходимого для "конструктора доступа конструктор" для частного конструктора частного внутреннего класса testClass$Inner. Разборка показывает, что полное имя этого класса указано правильно, поэтому непонятно, почему файл класса оказался в неправильном каталоге.

12
ответ дан 27 November 2019 в 18:27
поделиться

Еще одно место - если OuterClass $ 1 уже объявлен пользователем, OuterClass $ PrivateInnerClass в любом случае будет иметь его как аргумент конструктора:

public class OuterClass { 

    ... 

    public String getStringFromPrivateInner() { 
        PrivateInnerClass c = new PrivateInnerClass();
        Object o = new Object() {};
        return null;
    }
}

-

public java.lang.String getStringFromPrivateInner();
  Code:
   0:   new     #2; //class OuterClass$PrivateInnerClass
   3:   dup
   4:   aload_0
   5:   aconst_null
   6:   invokespecial   #3; //Method OuterClass$PrivateInnerClass."":
(LOuterClass;LOuterClass$1;)V
   9:   astore_1
   10:  new     #4; //class OuterClass$1
   13:  dup
   14:  aload_0
   15:  invokespecial   #5; //Method OuterClass$1."":(LOuterClass;)V
   18:  astore_2
   19:  aconst_null
   20:  areturn
0
ответ дан 27 November 2019 в 18:27
поделиться

Я использую меньший фрагмент polygenelubricants.

Помните, что в байт-коде нет концепции вложенных классов; однако байт-код учитывает модификаторы доступа. Проблема, которую здесь пытается обойти компилятор, заключается в том, что метод instantiate () должен создать новый экземпляр PrivateInnerClass . Однако OuterClass не имеет доступа к конструктору PrivateInnerClass ( OuterClass $ PrivateInnerClass будет сгенерирован как класс, защищенный пакетом, без открытого конструктора ).

Так что же может делать компилятор? Очевидное решение - изменить PrivateInnerClass на конструктор, защищенный пакетом.Проблема здесь в том, что это позволит любому другому коду, который взаимодействует с классом, создать новый экземпляр PrivateInnerClass , даже если он явно объявлен как закрытый!

Чтобы предотвратить это, компилятор javac проделывает небольшую хитрость: вместо того, чтобы сделать обычный конструктор PrivateInnerClass видимым из других классов, он оставляет его скрытым (на самом деле он не определяет его в все, но то же самое снаружи). Вместо этого он создает новый конструктор, который получает дополнительный параметр специального типа OuterClass $ 1 .

Теперь, если вы посмотрите на instantiate () , он вызывает этот новый конструктор. Фактически он отправляет null в качестве второго параметра (типа OuterClass $ 1 ) - этот параметр используется только для указания того, что именно этот конструктор должен быть вызван.

Итак, зачем создавать новый тип для 2-го параметра? Почему бы не использовать, скажем, Объект ? Он используется только для того, чтобы отличить его от обычного конструктора, и null все равно передается! И ответ таков: поскольку OuterClass $ 1 является частным для OuterClass, законный компилятор никогда не позволит пользователю вызывать специальный конструктор OuterClass $ PrivateInnerClass в качестве одного из требуемых типов параметров, Внешний класс $ 1 скрыт.

Я предполагаю, что компилятор JDT использует другой метод для решения той же проблемы.

26
ответ дан 27 November 2019 в 18:27
поделиться

Основываясь на ответе полигенных смазочных материалов, я мог бы предположить, что этот загадочный класс не позволяет кому-либо еще (то есть вне OuterClass ) создать экземпляр OuterClass $ PrivateInnerClass , потому что они не ' У меня есть доступ к OuterClass $ 1 .

7
ответ дан 27 November 2019 в 18:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: