为什么包保护方法在同一个包中不可见?
假设我们有两个包p1
和p2
和p2.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");
}
}
让我们延长M12
与p2.B
:
package p2;
public class B extends M12 {
public void doSomething() {
method1();
method2();
}
}
这给出了一个编译错误作为method1
,是套餐的p1
内的保护在p2
中不可见。 method2
显示没有问题。
现在让我们来延长p2.M12
与p1.A
:
package p1;
import p2.M12;
public class A extends M12 {
public void doSomething() {
method1();
method2();
}
}
在这里我得到一个编译错误,两个method2()
(这是可以理解的)和method1()
:
我的问题是:为什么是method1
,它在封装中受封装保护p1
在类别A
中不可见,来自同一封装p1
?
首先,什么是类的成员?所述Java Language Specification状态
类体可含有类的成员的声明,即 是,字段(§8.3),方法(8.4节),类(第8.5节),和接口 (第8.5节) 。
它们是由什么组成的?类类型的JLS states
成员是以下所有:
- 成员继承其直接超类(§8.1.4),除在类对象,其具有没有直接超类
- 从任何直接超接口继承的成员(§8.1.5)
- 成员在类的主体中声明(§8.1.6)
它还提到
被声明
protected
或public
是 继承通过在比在其它 一个包中声明子类的一个类的成员才哪个类被声明。
所有这一切都在chapter on Inheritance
从其直接超改写类
C
继承超类的所有具体方法m
(静态和实例)对所有的 的以下为真:
m
是直接超级会员女士的C
。m
是public
,protected
或在包裹访问中声明与C`相同的包中。- 在
C
中声明的任何方法的签名都是m
签名的子签名(第8.4.2节)。
M1
类的成员是method1
(和Object
所有方法)。 M12
,与其直接超类M1
处于不同的包中,不继承method1
。 M12
的成员因此只有method2
。
B
的直接超类是M12
并且在同一个包中。因此它继承了其成员method2
。 B
对method1
一无所知。如果您使用javac
编译了代码,则应该收到编译错误cannot find symbol
。 (看来Eclipse正试图猜测你在做什么。)
同样,A
的直接超类M12
,但是在不同的包中。由于这个原因,它不会继承method2
。 A
不知道任何关于method1
或method2
,因为它没有继承它们。这两个都是无法找到的符号。
只是注意到,其他答案确实提到了这一点,这就是为什么我从Krease提出的原始内容,而且,这是后期是如何给出我的@override注释示例的替代解释,如果继承是线性的过程,并且注释应该指向这篇文章的引用:如果你已经用javac编译你的代码,你将会收到一个找不到符号编译错误。 – Victor
我觉得要理解这种行为的最简单的方法是“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
您很容易忽略'A'也是*'M1'。 'M1'确实有一个方法'method1'在'p1'中可见。当然,我们可以让所有的东西都公开,并考虑这个FTFY,但这不是问题所在。你引用了一个访问级别的表 - 有“没有修饰符 - 包Y”。 'A'与'M1.method1()'在同一个包中,所以解释是什么? – lexicore
还要注意应该有所帮助的过度使用注释行为。 – Victor
我没有忽视。它似乎是同样的情况,它属于另一个答案,我赞成,因为有人已经低估了它。这是一个奇怪的继承情况 - 是A是M1,但它不是它从哪里得到它的行为,这是一种线性过程。我们可以在这里谈论一个子成员的访问修饰符,因为它在行为上是相似的。假设你的子类扩展了方法的访问级别。它的孩子的孩子将无法恢复原来的访问级别,只能进一步扩展。奇怪的情况,如那些在深层次上会使继承不好的奇怪情况。 – Victor
这意味着一个孩子的班级会比其父母看到的多。这听起来很奇怪。 – luk2302
@ luk2302这个软件包保护的方法不在包中,也很奇怪。 – lexicore
这是我设法找到的JLS中最相关的部分:https://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-15.12.4.3 –