2013-03-04 56 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()); 
     } 
    } 
}