16

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

问题:为什么要合成一个私有构造函数和一个包私有构造函数?为什么不只合成包私有构造函数并完成它呢?

4

3 回答 3

13

如果你写这样的代码,

public class Outer {
      private class Inner {}
}

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

JLS 的第 8.8.9节要求这个构造函数,它说如果没有定义构造函数,则必须生成默认构造函数,在这种情况下,默认构造函数必须是私有的,

在类类型中,如果类被声明为 public,则默认构造函数被隐式赋予访问修饰符 public(第 6.6 节);如果该类被声明为受保护,则默认构造函数被隐式赋予访问修饰符 protected(第 6.6 节);如果该类被声明为私有,则默认构造函数被隐式赋予访问修饰符 private(第 6.6 节);否则,默认构造函数具有没有访问修饰符隐含的默认访问权限。

但是,当您使用以下代码在 Outer 中实例化 Inner 的实例时,

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

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

编译器引入的任何在源代码中没有相应构造的构造都必须标记为合成,但默认构造函数和类初始化方法除外。

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

于 2013-03-08T06:03:56.703 回答
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());
        }
    }
}
于 2013-03-08T10:20:34.367 回答
2

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

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

于 2013-03-04T10:33:25.253 回答