Java中包可见性的继承

TFu*_*uto 21 java inheritance overriding shadowing package-private

我正在寻找以下行为的解释:

  • 我有6个类,{aA,bB,cC,aD,bE,cF},每个类都有一个包可见的m()方法,该方法可以写出类名。
  • 我有一个带有主要方法的a.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"); }
}
Run Code Online (Sandbox Code Playgroud)

主类在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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这是输出:

((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
Run Code Online (Sandbox Code Playgroud)

这是我的问题:

1)我知道D.m()hides A.m(),但是强制转换为A暴露隐藏m()方法,是真的吗?或者是D.m()覆盖A.m()尽管事实,B.m()C.m()中断继承链?

((A)d).m();
D
Run Code Online (Sandbox Code Playgroud)

2)更糟糕的是,以下代码显示了覆盖的作用,为什么?

((A)e).m();
E
((A)f).m();
F
Run Code Online (Sandbox Code Playgroud)

而为什么不在这一部分:

((A)a).m();
A
((A)b).m();
A
((A)c).m();
A
Run Code Online (Sandbox Code Playgroud)

还有这个?

((D)d).m();
D
((D)e).m();
D
((D)f).m();
D
Run Code Online (Sandbox Code Playgroud)

我正在使用OpenJDK javac 11.0.2。


编辑:第一个问题是如何通过使用默认(程序包)可见性范围覆盖方法来回答的

如果满足以下所有条件,则在类D中声明或由类D继承的实例方法mD从类D覆盖另一个在类A中声明的方法mA:

  • A是D的超类。
  • D不继承mA(因为跨越包边界)
  • mD的签名是mA签名的子签名(第8.4.2节)。
  • 以下之一是正确的:[...]
    • 在与D相同的程序包中声明具有访问程序包的mA(在这种情况下),并且D声明mD或mA是D的直接超类的成员。[...]

但是:第二个问题仍未解决。

And*_*lko 7

我知道D.m()隐藏A.m(),但是强制转换为A应该暴露隐藏的m()方法,是真的吗?

没有诸如隐藏实例(非静态)方法之类的东西。在这里,这是阴影的示例。强制转换为A在大多数地方只是帮助解决歧义(如c.m()原样可以同时参考A#mC#m[这是不是从存取a]),否则会导致编译错误。

或者是D.m()覆盖A.m()尽管事实,B.m()C.m()中断继承链?

b.m()是一个模棱两可的调用,因为两者都适用,如果您不考虑可见性因素A#mB#m则两者均适用。同样的道理c.m()((A)b).m()((A)c).m()明确指出A#m呼叫者可以访问的内容。

((A)d).m()更有趣:既AD驻留在同一封装(因此,可访问的[这是由两个以上的情况下不同的])和D间接地继承A。在动态分派期间,Java将能够调用,D#m因为D#m实际上是重写A#m并且没有理由不调用它(尽管继承路径上发生了混乱(请记住,由于可见性问题,也B#m没有C#m重写A#m))。

更糟糕的是,以下代码显示了覆盖的效果,为什么呢?

我无法解释这一点,因为这不是我期望的行为。

我敢说

((A)e).m();
((A)f).m();
Run Code Online (Sandbox Code Playgroud)

应该与的结果相同

((D)e).m();
((D)f).m();
Run Code Online (Sandbox Code Playgroud)

这是

D
D
Run Code Online (Sandbox Code Playgroud)

因为没有办法来访问包私有方法bca


TFu*_*uto 3

我报告了这个问题,并确认了多个 Java 版本的错误。

错误报告

我将此答案标记为解决方案,但要感谢大家的所有答案和消息,我学到了很多。:-)