Java:跨包的受保护的访问


问题内容

我想了解以下示例中的情况(通过包从子类外部访问受保护的成员)。

我知道对于包外部的类,子类只能通过继承才能看到受保护的成员。

有两个包:package1package2

  1. package1ProtectedClass.java
        package org.test.package1;

    public class ProtectedClass {

        protected void foo () {
            System.out.println("foo");
        }
    }
  1. package2ExtendsprotectedClass.java
        package org.test.package2;

    import org.test.package1.ProtectedClass;

    public class ExtendsprotectedClass  extends ProtectedClass {

        public void boo() {
            foo(); // This works, 
                   // since protected method is visible through inheritance
        }

        public static void main(String[] args) {
            ExtendsprotectedClass epc = new ExtendsprotectedClass();
            epc.foo(); // Why is this working? 
                       // Since it is accessed through a reference,
                       // foo() should not be visible, right?
        }
    }
  1. package2UsesExtendedClass.java
        package org.test.package2;

    public class UsesExtendedClass {

        public static void main(String[] args) {
            ExtendsprotectedClass epc = new ExtendsprotectedClass();
            epc.foo(); // CompilationError: 
                       // The method foo() from the type ProtectedClass
                       // is not visible
        }
    }

可以理解,中的boo()方法ExtendsprotectedClass可以访问foo(),因为受保护的成员只能通过继承来访问。

我的问题是,为什么是foo()方法,通过在基准访问时,工作正常main()的方法ExtendsprotectedClass ,但
将无法正常工作 通过访问时epc的参考UsesExtendedClass


问题答案:

ExtendsprotectedClass该类中的代码被允许ProtectedClass通过type的引用访问受保护的成员ExtendsprotectedClass。从JLS第6.6.2节中

可以从包的外部访问对象的受保护成员或构造函数,而在包中只能通过负责该对象实现的代码声明该对象。

令C为声明受保护成员m的类。仅在C的子类S的主体内允许访问。此外,如果Id表示实例字段或实例方法,则:

  • 如果通过限定名称Q.Id(其中Q是ExpressionName)进行访问,则且仅当表达式Q的类型为S或S的子类时,才允许访问。[…]

UsesExtendedClass对的实现不负责ExtendsprotectedClass,因此最终调用失败。

编辑:这背后的原因是protected访问旨在帮助子类实现其所需的功能,从而比通常提供更多的对超类内部的访问。如果 所有
代码都可以使用,那将很接近将方法公开。基本上,可以信任子类不破坏封装;可以使用子类。它们在自己类型的对象中具有更多功能。公用API不应公开那些细节,但是受保护的API可以仅为给子类提供更多机会的目的。