我有一个Outer
类,其中private Inner
类。
在我的Outer
类方法中,我按如下方式实例化Inner
类:
Outer outer = new Outer();
Inner inner = outer.new Inner();
编译器将此代码转换为:
Outer outer = new Outer();
Inner inner = new Inner(outer, null);
使用反射显示Inner
类具有以下合成构造函数:
private Outer$Inner(Outer)
Outer$Inner(Outer,Outer$Inner)
由于Inner
类是private
,编译器会将private
构造函数添加到它,因此没有人可以实例化该类。但显然Outer
类应该能够实例化它,因此编译器会添加其他包私有构造函数,后者又调用私有构造函数。此外,由于package-private构造函数的名称中包含$
,因此普通的Java代码无法调用它。
问题:为什么要合成一个私有和一个包私有构造函数?为什么不合成package-private构造函数并用它来完成呢?
答案 0 :(得分:13)
如果您编写代码,
public class Outer {
private class Inner {}
}
您会注意到只有一个构造函数private Outer$Inner(Outer)
这个构造函数是Section 8.8.9 of the JLS所必需的,它表示如果没有定义构造函数,则必须生成默认构造函数,在这种情况下,默认构造函数必须是私有的,
在类类型中,如果该类声明为public,则为默认值 构造函数隐式赋予访问修饰符public(§6.6);如果 该类被声明为protected,然后是默认构造函数 隐式赋予访问修饰符保护(第6.6节);如果是这样的话 声明为private,然后隐式给出默认构造函数 access modifier private(§6.6);否则,默认构造函数具有 无访问修饰符隐含的默认访问权限。
但是,当您使用类似的代码
实例化Inner inside Outer的实例时public class Outer {
private class Inner {}
public String foo() {
return new Inner().toString();
}
}
编译器必须生成Outer可以合法调用的构造函数(您不能合法地调用私有默认构造函数,因为它是私有的)。因此编译器必须生成一个新的合成构造函数。根据{{3}}
,新构造函数必须是合成的编译器引入的任何没有的构造 源代码中的相应构造必须标记为 合成的,默认构造函数和类除外 初始化方法。
第二个构造函数在源代码中没有相应的构造,因此这个新的构造函数必须是合成的。仍然必须生成第一个私有构造函数,因为JLS需要私有默认构造函数。
答案 1 :(得分:3)
这不是一个答案,我认为sbridges已经很好地解决了这个问题。它只是一个产生您描述的行为的工作示例:
public class Outer {
private class Inner {
}
public static void main(String[] args) {
printConstructors();
//only one constructor is printed but two would appear if you
//uncommented the line below
//new Outer().new Inner();
}
private static void printConstructors() {
Constructor[] constructors = Outer.Inner.class.getDeclaredConstructors();
for (Constructor c : constructors) {
System.out.println(c.toGenericString());
}
}
}
答案 2 :(得分:2)
最可能的答案是尊重您在源代码中声明的内容。这样做仍然允许在声明它时通过反射使用私有构造函数。
这也避免了检查私有构造函数是否实际在Inner
类中调用。