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
ничего не содержит. Куда этот дополнительный класс прибывает из и почему он создается?
У меня нет ответа, но я могу подтвердить это и сократить фрагмент до следующего:
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
вложенные, и т. д.). Создайте следующий исходный текст в каталоге с именем 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
. Разборка показывает, что полное имя этого класса указано правильно, поэтому непонятно, почему файл класса оказался в неправильном каталоге.
Еще одно место - если 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
Я использую меньший фрагмент 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 использует другой метод для решения той же проблемы.
Основываясь на ответе полигенных смазочных материалов, я мог бы предположить, что этот загадочный класс не позволяет кому-либо еще (то есть вне OuterClass
) создать экземпляр OuterClass $ PrivateInnerClass
, потому что они не ' У меня есть доступ к OuterClass $ 1
.