Java访问修饰符,嵌套类

3

以下星座:

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

}

Sub类的私有构造函数可以调用Super类的私有构造函数。

为什么这样做是可以的,在这里得到了解答: 嵌套类中私有构造函数的作用域

因此,在Sub类的构造函数中调用私有方法setN,也应该是可以的,对吗?

为什么这样做不合法呢?

2个回答

6

就像超类构造函数一样,setNSub中是完全可访问的。这是因为,正如您链接的Q&A所解释的那样,在内部类中声明的private成员只要您在顶级类中就可以随处访问。因此问题出在其他地方。

如果您正在使用IntelliJ,则它给出的错误消息是“setN具有私有访问权限...”,这更有用,可以让您立即修复错误(并且setN是私有的也会发挥作用,正如您将看到的)。但是,javac的错误消息“找不到符号setN”表明实际上这与可访问性无关,编译器实际上找不到setN

根据spec,像setN这样的简单方法名

由单个UnqualifiedMethodIdentifier组成,其指定要调用的方法的名称。方法调用规则要求UnqualifiedMethodIdentifier在方法调用点上表示一个处于范围内的方法。
在Sub的构造函数中实际上不包括setN。setN的范围仅为Super。请参见规范中关于声明范围的Scope of a Declaration部分。
但是,Sub并没有从Super继承setN?实际上没有。私有成员不会被继承。有关继承条件,请参见this section
一个类C从其直接超类类型D继承所有具体方法m(静态和实例),对于这些方法,以下所有条件都为真:
  • m是D的成员。
  • m是公共的、受保护的或在与C相同的包中使用包访问声明的。
  • C中没有声明的方法具有作为D成员的m的签名的子签名(§8.4.2)。
如果您使用另一种形式(而不是简单名称)来引用Super.setN,例如super.setN(5);或((Super)this).setN(5);那么它可以编译得很好。

关于这些为什么起作用,请参见处理方法调用的编译时步骤1,在那里编译器确定应在哪种类型中搜索方法声明。如果要搜索的类型是Super,则它将被编译,因为Super实际上具有setN方法,而Sub没有从Super继承它,因此不具有它作为成员。

请注意,构造函数调用和方法调用在语言规范中完全分开处理,它们有完全不同的规则super();语法本质上是“硬编码”到语言中的 - 没有构造函数的“作用域”或“名称”之类的东西。只要您可以访问构造函数,就可以调用它。


非常感谢您的澄清和规范链接! Eclipse也会提示“不可见错误”,有点令人困惑。 我试着记住JLS 8.4.8。简而言之,私有方法不会被继承。 尽管如此,在嵌套类的情况下,可以通过使用“super”调用它们。 - coder

1
编译器理解类Super的作用域在AccessModifierTest中,但是编译器不知道任何类的被调用方法而没有引用。这就是oop语言设计的工作方式。主要好处是编译器可以通过这种方式消除方法调用的歧义。
因此,在类范围内调用私有构造函数时,编译器会理解它来自哪里,但是任何其他方法都需要对象引用才能访问。
为了使您的代码工作,您需要使用引用super.setN(5)来使其工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接