2013-03-04 77 views
16

我有一个Outer类具有private Inner类。的Java:私有内部类合成构造

以我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类应该能够实例化它,因此编译器添加了其他包私有构造函数,该构造函数又调用私有构造函数。另外,由于包名 - 私有构造函数的名称中包含$,因此普通Java代码无法调用它。

问:为什么合成一个私人和一个包私有构造?为什么不综合包私有构造函数,并完成它?

+0

@Noofiz这些构造函数是由编译器创建的,没有你明确地编码它们;因此我称他们为合成。 – shrini1000 2013-03-04 10:42:34

+0

@Noofiz如果你不明白这个问题,我建议你把它留给那些做的人。 – EJP 2013-03-04 10:43:00

+0

“外部$内部(外部,外部$内部)”是否真的正确?构造函数获取与参数相同的类的实例?为什么编译器会添加这样一个参数。 – 2013-03-04 11:10:09

回答

13

如果你写的代码一样,

public class Outer { 
     private class Inner {} 
} 

你会注意到,世界上只有一个构造函数private Outer$Inner(Outer)

此构造是由科8.8.9 of the JLS要求,它说,如果没有构造函数定义默认构造函数必须被生成,并且在这种情况下默认构造函数必须是私有的,

在类类型中,如果类是声明为public,那么默认的构造函数 隐含地赋予访问修饰符public(§6.6);如果 该类声明为受保护的,则默认构造函数为 隐式给定访问修饰符protected(第6.6节);如果这个类是 声明为private,那么默认的构造函数被隐式地赋予 访问修饰符private(§6.6);否则,默认构造函数具有 默认访问默认访问默认访问修饰符。

然而,当你你实例里面:外的一个实例,下面类似的代码,

public class Outer { 
    private class Inner {} 
     public String foo() { 
      return new Inner().toString(); 
     } 
} 

编译器必须生成一个构造函数,外部可以合法地调用(你不能合法调用私有默认构造函数,因为它是私有的)。所以编译器必须生成一个新的合成构造函数。新构造函数必须是合成的,根据section 13.1 of the JLS

由编译器引入的任何构建体,其不具有 在源代码对应的构造必须被标记为 合成,除了默认构造函数和类 初始化方法。

这第二个构造函数在源代码中没有对应的构造,所以这个新的构造函数必须是合成的。由于JLS需要私有默认构造函数,所以仍然必须生成第一个私有构造函数。

+0

为什么实例化Inner的新实例是非法的?外面仍然可以,对吧?另外,由于这个私有构造函数是由编译器生成的,为什么它不是一个合成构造函数? – shrini1000 2013-03-08 10:23:07

+0

重写我的答案。 – sbridges 2013-03-08 14:33:28

+0

这现在很清楚。谢谢! – shrini1000 2013-03-08 15:25:03

2

最可能的答案是尊重你在你的源代码声明。这样做仍然允许在声明时通过反射使用私有构造函数。

这也避免了检查是否私有构造函数在Inner类中实际调用。

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