Home > front end >  Java access modifiers, nested classes
Java access modifiers, nested classes

Time:01-08

The following constellation:

public class AccessModifierTest {

  private static class Super {
    private int n = 3;

    private Super() {}

    private void setN(int n) {
      this.n = n;
    }
  }

  private static class Sub extends Super {
    private Sub() {
      super(); // invoking private ctor is permitted
      // setN(5); but invoking private method is not permitted, why?
      // super.setN(5); this is fine too
    }
  }

  public static void main(String[] args) {
    Super superInstance = new Sub();
  }

}

The private ctor of Sub is allowed to invoke the private ctor of Super.

Why this is ok, is answered here: scope of private constructor in Nested Class

Because of this, shouldn't it be also fine to invoke the private method setN in the ctor of Sub?

Why is this not legal?

CodePudding user response:

Just like the superclass constructor, setN is perfectly accessible in Sub. This is because, as your linked Q&A explained, private members declared in an inner class are accessible everywhere as long as you are inside the top-level class. So the problem is somewhere else.

If you are using IntelliJ, the error message it gives is "setN has private access...", which is more useful and lets you fix the error immediately (and setN being private also plays a role as you will see). But javac's error message, "cannot find symbol setN", shows that this isn't actually about accessibility, and that the compiler can't actually find setN.

According to the spec, a simple method name like setN

consists of a single UnqualifiedMethodIdentifier which specifies the name of the method to be invoked. The rules of method invocation require that the UnqualifiedMethodIdentifier denotes a method that is in scope at the point of the method invocation.

setN is actually not in scope in the constructor of Sub. The scope of setN is only Super. See also Scope of a Declaration in the spec.

But doesn't Sub inherit setN from Super? Well, it doesn't. private members are not inherited. See this section for the conditions for inheritance.

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

  • m is a member of D.

  • 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 as a member of D.

If you use another form (instead of a simple name) to refer to Super.setN, like super.setN(5);, or ((Super)this).setN(5); then it can compile just fine.

For why these work, see the compile-time step 1 of processing a method call, where the compiler determines in which type it should search for a method declaration. If the type to search is Super, then it would compile, because Super actually has the setN method, whereas Sub does not inherit it from Super, so does not have it as a member.

Note that constructor calls and method calls are treated completely separately by the language spec and they have completely different rules. The super(); syntax is essentially "hardcoded" into the language - there is no "scope" or "names" of a constructor or anything like that. The constructor just has to be accessible for you to call it.

CodePudding user response:

Compiler understands the scope of the class Super is within the AccessModifierTest, but compiler does not know a callee method of any class without the reference. This is how oop based languages are designed to work. The main benefit is that compiler can remove the method call ambiguity through this.

So when a private constructor is called within a class scope compiler understands where it is coming from, but any other method requires the object reference to access.

to make your code work you need to use the reference super.setN(5) to make it work.

  •  Tags:  
  • java
  • Related