(編集:いくつかのコメントに答えるために答えを拡張しました)
コンパイラは内部クラスを受け取り、それらを最上位クラスに変換します。プライベート メソッドは内部クラスでのみ使用できるため、コンパイラはパッケージ レベルのアクセス権を持つ新しい「合成」メソッドを追加して、最上位クラスがアクセスできるようにする必要があります。
このようなもの ($ のものはコンパイラによって追加されます):
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 がこのようにする理由は、内部クラスをサポートするために VM の変更を必要としたくなかったためです。そのため、すべての変更をコンパイラ レベルで行う必要がありました。
コンパイラは内部クラスを受け取り、それを最上位クラスに変換します (したがって、VM レベルには内部クラスなどはありません)。次に、コンパイラは新しい「転送」メソッドも生成する必要があります。同じパッケージ内のクラスのみがアクセスできるように、パッケージ レベル (パブリックではない) で作成されます。コンパイラは、プライベート メソッドへのメソッド呼び出しも、生成された「転送」メソッドに更新しました。
メソッドを「パッケージ」として宣言することで、コンパイラにメソッドを生成させることを避けることができます (public、private、および protected の不在)。その欠点は、パッケージ内のどのクラスでもメソッドを呼び出すことができることです。
編集:
はい、生成された (合成) メソッドを呼び出すことはできますが、これは行わないでください!:
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);
}
}
}
}