2013-10-02 102 views
3

我不能創建B對象,但爲什麼?使用反射實例化內部類時的InstantiationException。爲什麼?

public class AFactory { 

    public int currentRange; 

    private abstract class A { 
     protected final Object range = currentRange; 

     public int congreteRange = 28; 
    } 

    public class B extends A { 
     public int congreteRange = 42; 
    } 

    synchronized A createNew(Class<? extends A> clazz) throws Exception { 
     // EDIT: there is accessible default constructor 
     currentRange = clazz.newInstance().congreteRange; 
     return clazz.newInstance(); 
    } 

    public static void main(String[] args) throws Exception { 
     AFactory factory = new AFactory(); 
     System.out.println(factory.createNew(B.class).range); 
    } 
} 

的例外是:

Exception in thread "main" java.lang.InstantiationException: AFactory$B 
at java.lang.Class.newInstance0(Class.java:357) 
at java.lang.Class.newInstance(Class.java:325) 
at AFactory.createNew(AFactory.java:15) 
at AFactory.main(AFactory.java:21) 

回答

2

的問題是你試圖實例化一個內部類,您只能在外部類的實例訪問。內部類的構造函數採用封閉類的隱式隱藏instance。您可以通過分析這個簡單的類的字節代碼中看到它:

public class Demo { 
    class Test { 
    } 
} 

現在,編譯代碼:

javac Demo.java 

這將創建兩個類文件:

Demo.class 
Demo$Test.class 

運行以下命令查看Demo$Test.class的字節代碼:

javap -c . Demo$Test 

您將得到以下結果:

class Demo$Test { 
    final Demo this$0; 

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

所以,你看到的類的構造函數?它需要Demo作爲參數。所以,沒有0-arg構造函數。

但是,如果你讓你的內部類static,它會工作,因爲那麼你不需要任何封閉類的實例來調用內部類的構造函數。

隨着static內部類 - 備選:

public class AFactory { 

    public static int currentRange; 

    private static abstract class A { 
     protected final Object range = AFactory.currentRange; 
    } 

    public static class B extends A { 
     public int congreteRange = 42; 
    } 

    synchronized A createNew(Class<? extends B> clazz) throws Exception { 
     currentRange = clazz.newInstance().congreteRange; 
     return clazz.newInstance(); 
    } 

    public static void main(String[] args) throws Exception { 
     AFactory factory = new AFactory(); 
     System.out.println(factory.createNew(B.class).range); 
    } 
} 

與非static內部類 - 最終代碼:

如果你不想讓他們static,那麼你將不得不首先創建一個封閉類的實例。並將其傳遞給內部類的構造函數。要獲得內部類的構造函數,可以使用Class#getDeclaredConstructor方法。

現在,您將不得不修改您的工廠方法,以Constructor作爲參數。修改您的代碼,如下所示:

public class AFactory { 

    public int currentRange; 

    private abstract class A { 
     protected final Object range = currentRange; 
    } 

    public class B extends A { 
     public int congreteRange = 42; 
    } 

    synchronized A createNew(Constructor<? extends A> ctor) throws Exception { 
     // Pass `this` as argument to constructor. 
     // `this` is reference to current enclosing instance 
     return ctor.newInstance(this); 
    } 

    public static void main(String[] args) throws Exception { 
     AFactory factory = new AFactory(); 

     // Get constructor of the class with `AFactory` as parameter 
     Class<B> bClazz = B.class; 
     Constructor<B> ctor = bClazz.getDeclaredConstructor(AFactory.class); 

     System.out.println(factory.createNew(ctor)); 
    } 
} 
+0

但是我已經有了一個外部類'new AFactory()'的實例! –

+0

@SotiriosDelimanolis。這是現在的靜態領域。我改變了它。 –

+0

@Rohit Jain - 你確定嗎?我認爲可以創建一個內部類的實例。它是公共的,它有一個要附加到的'AFactory'()實例。 – Avi

1

您不能創建一個,因爲你定義clazz是擴展BA沒有延伸B類型 - 這是周圍的其他方式(B延伸A)。

,則應該更換createNew簽名是:

synchronized A createNew(Class<? extends A> clazz) throws Exception 
+0

More over clazz.newInstance()'被調用兩次。 –

+0

我改變了我的問題,同樣的例外。 –

-1

將您的類和變量更改爲靜態。

public class AFactory { 

public static int currentRange; 

private static abstract class A { 
    protected final Object range = currentRange; 
} 

public static class B extends A { 

    public int congreteRange = 42; 
} 

synchronized A createNew(Class<? extends B> clazz) throws Exception { 

    currentRange = clazz.newInstance().congreteRange; 
    return clazz.newInstance(); 
} 

public static void main(String[] args) throws Exception { 
    AFactory factory = new AFactory(); 
    System.out.println(factory.createNew(B.class).range); 
} 
} 

和輸出是。

+0

*使他們全部靜態* - 您的默認答案:D?我喜歡禁止任何其他類來創建實例A. –

+0

@PeterRader:「我喜歡禁止任何其他類創建實例A.」那是什麼意思?既然B是公開的,即使B是一個非靜態的內部類,任何人都可以創建它的一個實例,只要他們有一個'AFactory'的實例。 – newacct

+0

你是對的,我想我禁止任何其他類在沒有'AFactory'實例的情況下創建A的實例。 –

相關問題