为什么内部类使私有方法可访问?

2022-09-01 09:43:21

我不明白为什么会编译。f() 和 g() 从内部类中是可见的,尽管它是私有的。他们是否因为是内在的阶级而受到特殊对待?

如果 A 和 B 不是静态类,则它仍然是相同的。

class NotPrivate {
    private static class A {
        private void f() {
            new B().g();
        }
    }

    private static class B {
        private void g() {
            new A().f();
        }
    }
}

答案 1

(编辑:扩展了对答案的回答部分评论)

编译器获取内部类并将其转换为顶级类。由于私有方法仅对内部类可用,因此编译器必须添加具有包级别访问权限的新“综合”方法,以便顶级类可以访问它。

像这样的东西($是由编译器添加的):

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 级别没有内部类)。然后,编译器还必须生成新的“转发”方法。它们是在包级别(非公共)创建的,以确保只有同一包中的类才能访问它们。编译器还将对私有方法的方法调用更新为生成的“转发”方法。

您可以避免让编译器生成方法,我将方法声明为“包”(缺少公共,私有和受保护)。这样做的缺点是包中的任何类都可以调用这些方法。

编辑:

是的,您可以调用生成的(合成)方法,但不要这样做!

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);
            }
        }
    }
}

答案 2

我认为这句话很好地总结了这一点:

...内部类可以访问声明类的所有成员,甚至是私有成员。事实上,内部类本身被称为类的成员;因此,遵循面向对象工程的规则,它应该有权访问该类的所有成员。

由此而下,由于两个内部类实际上只是包含类的一部分,因此它们也应该能够访问彼此的私有成员。


推荐