Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheritance at package visibility in Java

Tags:

I am looking for an explanation for the following behavior:

  • I have 6 classes, {a.A,b.B,c.C,a.D,b.E,c.F}, each having a package visible m() method that writes out the class name.
  • I have an a.Main class with a main method that does some testing of these classes.
  • The output seems to not follow proper inheritance rules.

Here are the classes:

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

The Main class is in 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();
    }
}

And here is the output:

((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

And here are my questions:

1) I understand that D.m() hides A.m(), but a cast to A should expose the hidden m() method, is that true? Or is D.m() overrides A.m() in spite of the fact that B.m() and C.m() breaks the inheritance chain?

((A)d).m();
D

2) Even worse, the following code shows overriding in effect, why?

((A)e).m();
E
((A)f).m();
F

And why not in this part:

((A)a).m();
A
((A)b).m();
A
((A)c).m();
A

and this one?

((D)d).m();
D
((D)e).m();
D
((D)f).m();
D

I am using OpenJDK javac 11.0.2.


EDIT: The first question is answered by How to override a method with default (package) visibility scope?

An instance method mD declared in or inherited by class D, overrides from D another method mA declared in class A, iff all of the following are true:

  • A is a superclass of D.
  • D does not inherit mA (because crossing package boundaries)
  • The signature of mD is a subsignature (§8.4.2) of the signature of mA.
  • One of the following is true: [...]
    • mA is declared with package access in the same package as D (this case), and either D declares mD or mA is a member of the direct superclass of D. [...]

BUT: the second question is still unresolved.

like image 935
TFuto Avatar asked Sep 22 '19 19:09

TFuto


People also ask

What is package visibility in Java?

From the Java Documentation, "[package visibility] indicates whether classes in the same package as the class (regardless of their parentage) have access to the member." In this example from javax. swing , package javax.

Can we inherit package in Java?

In Java, it is possible to inherit attributes and methods from one class to another. We group the "inheritance concept" into two categories: subclass (child) - the class that inherits from another class. superclass (parent) - the class being inherited from.

Does default member gets inherited across the package?

In other words, a default member may be accessed only if the class accessing the member belongs to the same package, whereas a protected member can be accessed (through inheritance) by a subclass even if the subclass is in a different package.

Can class of a package be inherited by a different package class?

Yes, they can always inherit the protected members of its superclass regardless of the package they are in.


1 Answers

I understand that D.m() hides A.m(), but a cast to A should expose the hidden m() method, is that true?

There is no such thing as hiding for instance (non-static) methods. Here, it's an example of shadowing. A cast to A in most places just helps to resolve the ambiguity (e.g. c.m() as is can refer to both A#m and C#m [which isn't accessible from a]) that otherwise would lead to a compilation error.

Or is D.m() overrides A.m() in spite of the fact that B.m() and C.m() breaks the inheritance chain?

b.m() is an ambiguous call because both A#m and B#m are applicable if you set the visibility factor aside. The same goes for c.m(). ((A)b).m() and ((A)c).m() clearly refer to A#m which is accessible for the caller.

((A)d).m() is more interesting: both A and D reside in the same package (thus, accessible [which is different from the two above cases]) and D indirectly inherits A. During dynamic dispatch, Java will be able to call D#m because D#m actually overrides A#m and there is no reason not to call it (despite the mess going on the inheritance path [remember that neither B#m nor C#m overrides A#m due to the visibility issue]).

Even worse, the following code shows overriding in effect, why?

I can't explain this because it's not the behaviour I expected.

I dare say that the result of

((A)e).m();
((A)f).m();

should be identical to the result of

((D)e).m();
((D)f).m();

which is

D
D

since there is no way to access the package-private methods in b and c from a.

like image 52
Andrew Tobilko Avatar answered Oct 02 '22 16:10

Andrew Tobilko