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

问题描述:

假设我们有两个包p1p2p2.M12扩展类p1.M1如下:为什么包保护方法在同一个包中不可见?

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

让我们延长M12p2.B

package p2; 

public class B extends M12 { 

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

这给出了一个编译错误作为method1,是套餐的p1内的保护在p2中不可见。 method2显示没有问题。

现在让我们来延长p2.M12p1.A

package p1; 

import p2.M12; 

public class A extends M12 { 

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

在这里我得到一个编译错误,两个method2()(这是可以理解的)method1()The method method1 from the type M1 is not visible

我的问题是:为什么是method1,它在封装中受封装保护p1在类别A中不可见,来自同一封装p1

+0

这意味着一个孩子的班级会比其父母看到的多。这听起来很奇怪。 – luk2302

+0

@ luk2302这个软件包保护的方法不在包中,也很奇怪。 – lexicore

+0

这是我设法找到的JLS中最相关的部分:https://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-15.12.4.3 –

首先,什么是类的成员?所述Java Language Specification状态

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

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

成员是以下所有:

  • 成员继承其直接超类(§8.1.4),除在类对象,其具有没有直接超类
  • 从任何直接超接口继承的成员(§8.1.5)
  • 成员在类的主体中声明(§8.1.6)

它还提到

被声明protectedpublic是 继承通过在比在其它 一个包中声明子类的一个类的成员才哪个类被声明。

所有这一切都在chapter on Inheritance

其直接超改写类C继承超类的所有具体方法m (静态和实例)对所有的 的以下为真:

  • m是直接超级会员女士的C
  • mpublicprotected在包裹访问中声明与C`相同的包中。
  • C中声明的任何方法的签名都是m签名的子签名(第8.4.2节)。

M1类的成员是method1(和Object所有方法)。 M12,与其直接超类M1处于不同的包中,不继承method1M12的成员因此只有method2

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

同样,A的直接超类M12,但是在不同的包中。由于这个原因,它不会继承method2A不知道任何关于method1method2,因为它没有继承它们。这两个都是无法找到的符号。

+1

只是注意到,其他答案确实提到了这一点,这就是为什么我从Krease提出的原始内容,而且,这是后期是如何给出我的@override注释示例的替代解释,如果继承是线性的过程,并且注释应该指向这篇文章的引用:如果你已经用javac编译你的代码,你将会收到一个找不到符号编译错误。 – Victor

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

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

的,因为M1.method1是不可见的M12,这是不可见的,它的任何子类要么,像A

+1

“可见性必须通过类层次结构。” - 为什么? – lexicore

+0

“为什么” - 这是语言的工作方式 - 是否值得挖掘定义此规范的规范,或仅仅了解“这是语言设计工作的方式”? – Krease

+0

如果你有方便的话,我会对spec中的指针感兴趣。 –

我觉得要理解这种行为的最简单的方法是“A是M12”

当你声明的继承,你告诉你一个从M12获得其行为的想法,但M12没有所谓看得见的方法方法1。

让我们做一个实验的乐趣:

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

忘记..当你声明这样的方法,它被允许 - 如果你没有一个@覆盖就可以了。 但是,如果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  Y  Y  Y   Y 

protected Y  Y  Y   N 

no modifier Y  Y  N   N 

private  Y  N  N   N 
+0

您很容易忽略'A'也是*'M1'。 'M1'确实有一个方法'method1'在'p1'中可见。当然,我们可以让所有的东西都公开,并考虑这个FTFY,但这不是问题所在。你引用了一个访问级别的表 - 有“没有修饰符 - 包Y”。 'A'与'M1.method1()'在同一个包中,所以解释是什么? – lexicore

+0

还要注意应该有所帮助的过度使用注释行为。 – Victor

+0

我没有忽视。它似乎是同样的情况,它属于另一个答案,我赞成,因为有人已经低估了它。这是一个奇怪的继承情况 - 是A是M1,但它不是它从哪里得到它的行为,这是一种线性过程。我们可以在这里谈论一个子成员的访问修饰符,因为它在行为上是相似的。假设你的子类扩展了方法的访问级别。它的孩子的孩子将无法恢复原来的访问级别,只能进一步扩展。奇怪的情况,如那些在深层次上会使继承不好的奇怪情况。 – Victor