Crea un’istanza della class interna privata con la riflessione java

È ansible creare un’istanza di una class interna privata da un’altra class utilizzando la reflection Java. Per esempio se ho preso questo codice

public class Main { public static void main(String[] args) {} } class OtherClass { private class Test {} } 

è ansible creare un’istanza e accedere a Test dal metodo principale nella class principale.

Quando si usa il reflection, si trovano costruttori di quella class interna che prendono un’istanza della class esterna come argomento aggiuntivo (sempre il primo).

Vedi queste domande per informazioni correlate:

  • Istigazione della class interiore

  • Come posso creare un’istanza di una class membro attraverso la riflessione su Android

  • In Java, come posso accedere alla class esterna quando non sono nella class interna?

Esempio:

 import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class OuterClass { private class InnerClass { } public OuterClass() { super(); } public static void main(String[] args) { // instantiate outer class OuterClass outer = new OuterClass(); // List all available constructors. // We must use the method getDeclaredConstructors() instead // of getConstructors() to get also private constructors. for (Constructor ctor : OuterClass.InnerClass.class .getDeclaredConstructors()) { System.out.println(ctor); } try { // Try to get the constructor with the expected signature. Constructor ctor = OuterClass.InnerClass.class .getDeclaredConstructor(OuterClass.class); // This forces the security manager to allow a call ctor.setAccessible(true); // the call try { OuterClass.InnerClass inner = ctor.newInstance(outer); System.out.println(inner); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } 

Sì, puoi creare un’istanza di una class interna privata con la riflessione di Java. Per fare ciò, è necessario avere un’istanza di class esterna e invocare il costruttore della class interna che utilizzerà l’istanza di class esterna nel suo primo argomento.

 class OuterClass { private class InnerClass { { //this block is just to confirm that the inner object was created //it will be added to every constructor of this class System.out.println("inner object created"); } } } 

Quando non conosciamo il nome della class interna privata e assumiamo che abbia un costruttore senza argomenti:

 class Main { //no comment version public static Object giveMeInnerInstance() throws Exception{ OuterClass outerObject = new OuterClass(); Class innerClass = OuterClass.class.getDeclaredClasses()[0]; Constructor constructor = innerClass.getDeclaredConstructors()[0]; constructor.setAccessible(true); return constructor.newInstance(outerObject); } //commented version public static void main(String[] args) throws Exception { //we need an outer class object to use the inner object constructor //(the inner class object needs to know about its parent object) OuterClass outerObject = new OuterClass(); //let's get the inner class //(we know that the outer class has only one inner class, so we can use index 0) Class innerClass = OuterClass.class.getDeclaredClasses()[0]; //or if we know name of inner class we can use //Class innerClass = Class.forName("full.package.name.OuterClass$InnerClass") //since constructor so we could use it to pass instance of outer class and change //its accessibility. We can use this code to get default constructor of InnerClass //since we know that this is the only constructor here Constructor constructor = innerClass.getDeclaredConstructors()[0]; //we could also use //Constructor constructor = innerClass.getDeclaredConstructor(OuterClass.class); //the default constructor of the private class has same visibility that class has //so it is also private, so to be able to use it we need to make it accessible constructor.setAccessible(true); //now we are ready to create inner class instance Object innerObject = constructor.newInstance(outerObject); } } 

Ora possiamo chiarire questo codice se abbiamo informazioni come

  • nome della class interiore,
  • argomenti del costruttore

Quindi, invece di controllare la lista delle classi interne e scegliere la prima, possiamo ottenere la class interna selezionata usando il suo nome

 Class inner = Class.forName("our.pack.age.OuterClass$InnerClass") // ^^^^^^^^^^^ 

Allo stesso modo possiamo selezionare il costruttore che vogliamo usare invocando getDeclaredConstructor(outerType,rest,of,parameter,types) così se la nostra class interna assomiglierebbe

 class OuterClass { private class InnerClass { private int x; public InnerClass(int x) { this.x = x; System.out.println("inner object created"); } } } 

il nostro codice potrebbe essere

 class ReflectionDemo { //no comment version public static Object giveMeInnerInstance() throws Exception{ OuterClass outerObject = new OuterClass(); Class innerClass = Class.forName("com.stackoverflow.q14112166.OuterClass$InnerClass"); Constructor constructor = innerClass.getDeclaredConstructor(OuterClass.class, int.class); constructor.setAccessible(true); return constructor.newInstance(outerObject,42); } public static Object getFieldValue(Object obj, String fieldName) throws Exception{ Class clazz = obj.getClass(); Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); return field.get(obj); } //lets test our code public static void main(String[] args) throws Exception { Object innerClassObject = giveMeInnerInstance(); System.out.println(getFieldValue(innerClassObject, "x")); } } 

Produzione:

 inner object created 42