为什么在同一个包中看不到包保护方法?

假设我们有两个包p1p2以及由p2.M12扩展的类p2.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(); } } 

这给出了一个编译错误,因为method1p1被包保护在p2不可见。 method2可见而没有问题。

现在让我们用p1.A扩展p1.A

 package p1; import p2.M12; public class A extends M12 { public void doSomething() { method1(); method2(); } } 

这里我得到了method2() (可以理解) method1()的编译错误: 类型M1的方法method1不可见

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

首先,什么是class级成员? Java语言规范说明

类主体可以包含类成员的声明,即字段(第8.3节),方法(第8.4节),类(第8.5节)和接口(第8.5节)。

他们是由什么组成的? JLS表示

类类型的成员都是以下所有成员:

  • 成员inheritance自其直接超类 (第8.1.4节),但类Object中没有直接超类
  • 成员inheritance自任何直接的超级接口(第8.1.5节)
  • 在class级正文中宣布的成员(§8.1.6)

它也提到了

只有声明为protectedpublic的类的成员才会被声明在声明类之外的包中声明的子类inheritance。

所有这些都在关于inheritance的章节中进行了重写

C 从其直接超类inheritance超类的所有具体方法m (静态和实例),以下所有条件都为真:

  • mC的直接超类的成员。
  • m与C`在同一个包中的 publicprotected声明的包访问
  • C中声明的方法没有签名是m的签名的子签名(第8.4.2节)。

M1类的成员是method1 (以及Object所有方法)。 M12与其直接超类M1 ,不会inheritance方法1。 因此, M12的成员只是方法2。

B的直接超类是M12并且在同一个包中。 因此它inheritance了其成员method2Bmethod1一无所知。 如果您使用javac编译了代码,则会收到cannot find symbol编译错误的错误。 (似乎Eclipse试图猜测你想要做什么。)

类似地, A的直接超类是M12 ,但是在不同的包中。 由于这个原因,它不inheritancemethod2Amethod1method2一无所知,因为它没有inheritance它们。 这两个都是无法找到的符号。

我认为理解这种行为的最简单方法是“A是M12”的想法

当您声明inheritance时,您告诉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 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 ):

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

访问级别

 Modifier Class Package Subclass World public YYYY protected YYYN no modifier YYNN private YNNN 

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

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

M12M1.method1 ,它对它的任何子类都不可见,就像A一样。