java - Java中包可见性的继承
问题描述
我正在寻找以下行为的解释:
- 我有 6 个类,{aA,bB,cC,aD,bE,cF},每个类都有一个包可见 m() 方法来写出类名。
- 我有一个 a.Main 类,它有一个对这些类进行一些测试的 main 方法。
- 输出似乎没有遵循正确的继承规则。
以下是课程:
package a;
public class A {
void m() { System.out.println("A"); }
}
// ------
package b;
import a.A;
public class B extends A {
void m() { System.out.println("B"); }
}
// ------
package c;
import b.B;
public class C extends B {
void m() { System.out.println("C"); }
}
// ------
package a;
import c.C;
public class D extends C {
void m() { System.out.println("D"); }
}
// ------
package b;
import a.D;
public class E extends D {
void m() { System.out.println("E"); }
}
// ------
package c;
import b.E;
public class F extends E {
void m() { System.out.println("F"); }
}
主要课程在package a
:
package a;
import b.B;
import b.E;
import c.C;
import c.F;
public class Main {
public static void main(String[] args) {
A a = new A();
B b = new B();
C c = new C();
D d = new D();
E e = new E();
F f = new F();
System.out.println("((A)a).m();"); ((A)a).m();
System.out.println("((A)b).m();"); ((A)b).m();
System.out.println("((A)c).m();"); ((A)c).m();
System.out.println("((A)d).m();"); ((A)d).m();
System.out.println("((A)e).m();"); ((A)e).m();
System.out.println("((A)f).m();"); ((A)f).m();
System.out.println("((D)d).m();"); ((D)d).m();
System.out.println("((D)e).m();"); ((D)e).m();
System.out.println("((D)f).m();"); ((D)f).m();
}
}
这是输出:
((A)a).m();
A
((A)b).m();
A
((A)c).m();
A
((A)d).m();
D
((A)e).m();
E
((A)f).m();
F
((D)d).m();
D
((D)e).m();
D
((D)f).m();
D
这是我的问题:
1)我知道D.m()
hides A.m()
,但是演员A
应该暴露 hiddenm()
方法,这是真的吗?还是尽管打破了继承链,但还是会被D.m()
覆盖?A.m()
B.m()
C.m()
((A)d).m();
D
2)更糟糕的是,下面的代码显示了覆盖效果,为什么?
((A)e).m();
E
((A)f).m();
F
为什么不在这部分:
((A)a).m();
A
((A)b).m();
A
((A)c).m();
A
和这个?
((D)d).m();
D
((D)e).m();
D
((D)f).m();
D
我正在使用 OpenJDK javac 11.0.2。
编辑:第一个问题由如何覆盖具有默认(包)可见性范围的方法来回答?
在类 D 中声明或继承的实例方法 mD,从 D 覆盖在类 A 中声明的另一个方法 mA,如果以下所有条件都为真:
- A 是 D 的超类。
- D 不继承 mA(因为跨越封装边界)
- mD 的签名是 mA 签名的子签名(第 8.4.2 节)。
- 以下情况之一是正确的:[...]
- mA 在与 D 相同的包中声明为具有包访问权限(本例),并且 D 声明 mD 或 mA 是 D 的直接超类的成员。 [...]
但是:第二个问题仍未解决。
解决方案
我知道
D.m()
hidesA.m()
,但是演员A
应该暴露 hiddenm()
方法,是真的吗?
没有隐藏例如(非静态)方法的东西。在这里,它是一个阴影的例子。在大多数地方,强制转换A
只是有助于解决歧义(例如c.m()
,原样可以同时引用A#m
和C#m
[无法从a
] 访问),否则会导致编译错误。
还是尽管打破了继承链,但还是会被
D.m()
覆盖?A.m()
B.m()
C.m()
b.m()
是一个模棱两可的电话,因为如果您将可见性因素放在一边,两者都适用A#m
。B#m
也是如此c.m()
。((A)b).m()
并((A)c).m()
清楚地指出A#m
调用者可以访问哪个。
((A)d).m()
更有趣的是:两者A
和D
都驻留在同一个包中(因此,可访问[与上述两种情况不同])并D
间接继承A
. 在动态调度期间,Java 将能够调用D#m
,因为D#m
实际上覆盖A#m
并且没有理由不调用它(尽管继承路径上的混乱[请记住,由于可见性问题,既不B#m
也不C#m
覆盖])。A#m
更糟糕的是,下面的代码显示了覆盖效果,为什么?
我无法解释这一点,因为这不是我所期望的行为。
我敢说结果
((A)e).m();
((A)f).m();
应该与结果相同
((D)e).m();
((D)f).m();
这是
D
D
因为无法b
从c
.a
推荐阅读
- mysql - 如何创建一个 MySQL 临时表并在一个查询中显示其数据?
- python - 无法在 VS Code 中运行 python 脚本
- mysql - MySQL 跨多列计数,带有子查询,带有一个结果查询
- reactjs - 如何使历史的`goBack()`方法忽略哈希(#)?
- javascript - TypeError:无法读取未定义的属性“classList”
- android - 导航组件 - 淡入特定内容
- python - 无法使用 Jelastic 渲染 Django 应用程序
- emacs - emacs 子帧的奇怪渲染
- javascript - 如何使用 PHP 在 Carosol 3 中实现滑块卡?
- dialogflow-es - 对于对话流中的对话历史记录保持多长时间