Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is package-protected method not visible in the same package?

Assume we have two packages p1 and p2 and classes p1.M1 extended by p2.M12 as follows:

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

Let's extend M12 with p2.B:

package p2;

public class B extends M12 {

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

This gives a compilation error as method1, being package-protected within p1 is not visible in p2. method2 is visible without problems.

Now let's extend p2.M12 with p1.A:

package p1;

import p2.M12;

public class A extends M12 {

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

Here I'm getting a compilation error for both method2() (which is understandable) and method1(): The method method1 from the type M1 is not visible

My question is: why is the method1 which is package-protected in the package p1 is not visible in the class A from the same package p1?

like image 914
lexicore Avatar asked Oct 12 '17 19:10

lexicore


3 Answers

First, what is a member of a class? The Java Language Specification states

A class body may contain declarations of members of the class, that is, fields (§8.3), methods (§8.4), classes (§8.5), and interfaces (§8.5).

And what are they composed of? The JLS states

The members of a class type are all of the following:

  • Members inherited from its direct superclass (§8.1.4), except in class Object, which has no direct superclass
  • Members inherited from any direct superinterfaces (§8.1.5)
  • Members declared in the body of the class (§8.1.6)

It also mentions

Only members of a class that are declared protected or public are inherited by subclasses declared in a package other than the one in which the class is declared.

All of this is reworded in the chapter on Inheritance

A class C inherits from its direct superclass all concrete methods m (both static and instance) of the superclass for which all of the following are true:

  • m is a member of the direct superclass of C.
  • m is public, protected, or declared with package access in the same package as C`.
  • No method declared in C has a signature that is a subsignature (§8.4.2) of the signature of m.

The members of class M1 are method1 (and all the methods of Object). M12, being in a different package than its direct superclass, M1, does not inherit method1. The members of M12 are therefore only method2.

The direct superclass of B is M12 and is in the same package. It therefore inherits its member, method2. B knows nothing about method1. If you had compiled your code with javac, you would have received a cannot find symbol compilation error instead. (It seems Eclipse is trying to guess what you were trying to do.)

Similarly, the direct superclass of A is M12, but is in a different package. It does not inherit method2 for that reason. A doesn't know anything about method1 or method2 because it didn't inherit them. Both of those symbols cannot be found.

like image 109
Sotirios Delimanolis Avatar answered Oct 18 '22 02:10

Sotirios Delimanolis


I think the easiest way to understand this behavior is the idea of "A is a M12"

When you declared the inheritance, you told A to get its behavior from M12, but M12 has no visible method called method1.

Let's do an experiment for fun:

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

Forget A.. when you declare such method, it is allowed - if you do not have an @Override on it. however, if M1 was:

public class M1 {
    public void method1() {
        System.out.println("Method 1 called");
    }
}

you could have:

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

    void method2() {
        System.out.println("Method 2 called");
    }
}

now, back to the original code for M1 and M2 with the method re-declared and method one made public:

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

    public void method2() {
        System.out.println("Method 2 called");
    }
}

Then, you would be able to have

public class A extends M12 {

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

ok, this is a trivial case, but would be missing to complete the sequense... bottom line, semantically, you could explain by the IS relationship.


If more is needed (https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html):

The following table shows the access to members permitted by each modifier.

Access Levels

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
like image 23
Victor Avatar answered Oct 18 '22 04:10

Victor


Visibility must flow through the class hierarchy.

The class hierarchy is A --> M12 --> M1

The since M1.method1 is not visible to M12, it's not visible to any subclasses of it either, like A.

like image 1
Krease Avatar answered Oct 18 '22 04:10

Krease