2013-10-02 2 views
3

Я не могу создать B-объект, но почему?InstantationException при создании внутреннего класса с использованием отражения. Зачем?

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 быть типа, который простирается B и A не распространяется B - это наоборот (B продолжается A).

Вы должны изменить подпись createNew быть:

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

Подробнее о '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

Вы правы, мне нравится запрещать любые другие классы делать экземпляры A без экземпляра 'AFactory'. –

Смежные вопросы