为什么包保护方法在同一个包中不可见?

Why is package-protected method not visible in the same package?

假设我们有两个包 p1p2 和 classes p1.M1p2.M12 扩展如下:

package p1;

public class M1 {
    void method1() {
        System.out.println("Method 1 called");
    }
}


package p2;

import p1.M1;

public class M12 extends M1 {
    void method2() {
        System.out.println("Method 2 called");
    }
}

让我们用 p2.B 扩展 M12:

package p2;

public class B extends M12 {

    public void doSomething()  {
        method1();
        method2();
    }
} 

这会产生编译错误,因为 method1,在 p1 中受包保护在 p2 中不可见。 method2 可见,没有问题。

现在让我们用 p1.A 扩展 p2.M12:

package p1;

import p2.M12;

public class A extends M12 {

    public void doSomething() {
        method1();
        method2();
    }
}

method2()(这是可以理解的)method1()的编译错误:

我的问题是:为什么在包 p1 中受包保护的 method1 在同一包 class A 中不可见 p1?

可见性必须流经 class 层次结构。

class 层次结构是 A --> M12 --> M1

由于 M1.method1M12 不可见,因此它的任何子 class 也不可见,例如 A.

我认为理解这种行为的最简单方法是 "A is a M12"

当你声明继承时,你告诉A从M12获取它的行为,但是M12没有可见的方法method1。

我们来做个有趣的实验:

public class M12 extends p1.M1 {
    public void method1() {
        System.out.println("Method 1 called");
    }
    void method2() {
        System.out.println("Method 2 called");
    }
}

忘记 A.. 当您声明这样的方法时,它是允许的 - 如果您没有 @Override。 但是,如果 M1 是:

public class M1 {
    public void method1() {
        System.out.println("Method 1 called");
    }
}

你可以:

public class M12 extends p1.M1 {
    @Override
    public void method1() {
        System.out.println("Method 1 called");
    }

    void method2() {
        System.out.println("Method 2 called");
    }
}

现在,回到 M1 和 M2 的原始代码,重新声明方法并创建方法一 public:

public class M12 extends p1.M1 {
    public void method1() {
        System.out.println("Method 1 called");
    }

    public void method2() {
        System.out.println("Method 2 called");
    }
}

那么,您就可以

public class A extends M12 {

    public void doSomething() {
        method1();
        method2();
    }
}

好吧,这是一个微不足道的案例,但要完成序列就缺少了…… 最重要的是,从语义上讲,您可以通过 IS 关系来解释。


如果需要更多(https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html):

以下table显示了每个修饰符允许的成员访问权限。

访问级别

Modifier    Class   Package Subclass    World

public      Y       Y       Y           Y

protected   Y       Y       Y           N

no modifier Y       Y       N           N

private     Y       N       N           N

首先,什么是class的成员? Java Language Specification 状态

A class body may contain declarations of members of the class, that is, fields (§8.3), methods (§8.4), classes (§8.5), and interfaces (§8.5).

它们是由什么组成的? JLS states

The members of a class type are all of the following:

  • Members inherited from its direct superclass (§8.1.4), except in class Object, which has no direct superclass
  • Members inherited from any direct superinterfaces (§8.1.5)
  • Members declared in the body of the class (§8.1.6)

它还提到

Only members of a class that are declared protected or public are inherited by subclasses declared in a package other than the one in which the class is declared.

所有这些都在 chapter on Inheritance

中改写了

A class C inherits from its direct superclass all concrete methods m (both static and instance) of the superclass for which all of the following are true:

  • m is a member of the direct superclass of C.
  • m is public, protected, or declared with package access in the same package as C`.
  • No method declared in C has a signature that is a subsignature (§8.4.2) of the signature of m.

classM1的成员是method1(以及Object的所有方法)。 M12,与它的直接超级 class、M1 在不同的包中,不继承 method1。因此 M12 的成员只有 method2.

B的直接superclass是M12,在同一个包里。因此它继承了它的成员 method2Bmethod1 一无所知。如果您使用 javac 编译代码,则会收到 cannot find symbol 编译错误。 (似乎 Eclipse 正在尝试猜测您要做什么。)

同样,A的直接superclass是M12,只是在不同的包中。出于这个原因,它不会继承 method2Amethod1method2 一无所知,因为它没有继承它们。找不到这两个符号。