私有内部类的默认构造函数是否具有形式化参数?

2022-09-01 23:51:49

警告#1:这实际上是一个潜在的两部分:首先,私有内部类的构造函数是否有形式参数?如果是,为什么JLS说它没有?如果不是,如何/为什么不呢?

警告#2:这个问题不是为了猜测。我只在寻找权威的答案。

默认构造函数在 JLS 8.8.9 中定义,其中声明(部分):

默认构造函数没有形式参数,除非在非私有内部成员类中,其中默认构造函数隐式声明一个形式参数,表示类的紧闭实例(§8.8.1,§15.9.2,§15.9.3)。

(着重号后加)

“非私有”位对我来说似乎很奇怪:为了让内部类访问其封闭类中定义的字段,它需要对该实例的引用。无论内部类是否为私有类,这都应该是相同的。

事实上,javac似乎同意我的观点,这与规范相矛盾。如果我编译这个:

public class Ctors {
  private class MyInner {
  }
}

...并运行 ,然后我们看到一个带有单个形式参数的构造函数,用于封闭类的实例:javap -c -private

$ javap -c -private Ctors\$MyInner
Compiled from "Ctors.java"
class Ctors$MyInner {
  final Ctors this$0;

  private Ctors$MyInner(Ctors);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1        // Field this$0:LCtors;
       5: aload_0
       6: invokespecial #2        // Method java/lang/Object."<init>":()V
       9: return
}

作为参考,这是在Oracle JDK 1.8.0_05上。

所以JLS说私有内部成员类的默认构造函数没有正式参数,但javac/javap说它有一个。(我对事物最自然的工作方式的理解也说它应该有一个,因为价值很小。哪个是对的,为什么JLS明确排除了私有内部类?


答案 1

实现和规范之间是有区别的。

在我看来,“除了”JLS声明

...除了在非私有的内部成员类中...

措辞不当。

这意味着,编译器不需要隐式声明一个形式参数来表示类的紧闭实例...但它可以


为什么在非私有内部成员类中需要隐式形式参数?

来自 JLS 8.8.1

成员类可能已由编译器发出,该编译器与类实例创建表达式的编译器不同。因此,创建表达式的编译器必须有一种标准方法将引用(表示立即封闭的实例)传递给成员类的构造函数

例如,如果我使用第一个编译器编译此内部类:

package p1;
public class Ctors {
    public class MyInner {
    }
}

如果我想用另一个编译器编译这个子类:

package p2;

import p1.Ctors;

public class SubCtors {
    public SubCtors() {
        new Ctors();
    }
}

第二个编译器必须能够将默认构造函数与形式化参数一起使用。在本例中,包含实例的封闭类的实例。SubCtors


为什么在非私有内部成员类中不需要隐式形式参数?

因为非私有内部成员类始终由编译它的同一编译器访问。如图所示,javac 生成相同的构造函数,而不管类的可见性如何,但这不是必需的。另一个编译器实现可以自由选择另一种方式。

JLS 8.8.1中还有另一点,它非常符合同一条线。

本地类(不在静态上下文中)或匿名类的类实例创建表达式中,§15.9.2 指定本地/匿名类的立即封闭实例。本地/匿名类必须由与类实例创建表达式相同的编译器发出。该编译器可以随心所欲地表示立即封闭的实例。Java 编程语言无需在本地/匿名类的构造函数中隐式声明参数。


答案 2

推荐