(Правка: расширен ответ на некоторые комментарии)
Компилятор берет внутренние классы и превращает их в классы верхнего уровня. Поскольку закрытые методы доступны только для внутреннего класса, компилятор должен добавить новые «синтетические» методы, которые имеют доступ на уровне пакета, чтобы классы верхнего уровня имели к нему доступ.
Примерно так ($ добавляются компилятором):
class A
{
private void f()
{
final B b;
b = new B();
// call changed by the compiler
b.$g();
}
// method generated by the compiler - visible by classes in the same package
void $f()
{
f();
}
}
class B
{
private void g()
{
final A a;
a = new A();
// call changed by the compiler
a.$f();
}
// method generated by the compiler - visible by classes in the same package
void $g()
{
g();
}
}
Нестатические классы одинаковы, но к ним добавлена ссылка на внешний класс, чтобы к нему можно было вызывать методы.
Причина, по которой Java делает это таким образом, заключается в том, что они не хотели требовать изменения виртуальной машины для поддержки внутренних классов, поэтому все изменения должны были выполняться на уровне компилятора.
Компилятор берет внутренний класс и превращает его в класс верхнего уровня (таким образом, на уровне виртуальных машин не существует такого понятия, как внутренний класс). Затем компилятор также должен сгенерировать новые методы пересылки. Они сделаны на уровне пакета (не публично), чтобы обеспечить доступ к ним только для классов в одном пакете. Компилятор также обновил вызовы методов для закрытых методов для сгенерированных методов пересылки.
Вы можете избежать генерации метода компилятором, который объявит методы как "package" (отсутствие public, private и protected). Недостатком является то, что любой класс в пакете может вызывать методы.
Edit:
Да, вы можете вызвать сгенерированный (синтетический) метод, но НЕ ДЕЛАЙТЕ ЭТОГО!:
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Main
{
public static void main(final String[] argv)
throws Exception
{
final Class<?> clazz;
clazz = Class.forName("NotPrivate$A");
for(final Method method : clazz.getDeclaredMethods())
{
if(method.isSynthetic())
{
final Constructor constructor;
final Object instance;
constructor = clazz.getDeclaredConstructor(new Class[0]);
constructor.setAccessible(true);
instance = constructor.newInstance();
method.setAccessible(true);
method.invoke(null, instance);
}
}
}
}