Instancier une classe interne privée avec reflection java

Est-il possible d’instancier une classe interne privée à partir d’une autre classe en utilisant la reflection Java? Par exemple si j’ai pris ce code

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

est-il possible d’instancier et d’accéder à Test de la méthode main dans la classe main.

Lorsque vous utilisez la reflection, vous trouverez des constructeurs de cette classe interne prenant une instance de la classe externe comme argument supplémentaire (toujours le premier).

Voir ces questions pour des informations connexes:

  • Classe intérieure instanciée

  • Comment puis-je instancier une classe membre par reflection sur Android?

  • En Java, comment accéder à la classe externe lorsque je ne suis pas dans la classe interne?

Exemple:

 import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class OuterClass { private class InnerClass { } public OuterClass() { super(); } public static void main(Ssortingng[] 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(); } } } 

Oui, vous pouvez instancier une classe interne privée avec une reflection Java. Pour ce faire, vous devez avoir une instance de classe externe et appeler le constructeur de classe interne qui utilisera l’instance de classe externe dans son premier argument.

 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"); } } } 

Lorsque nous ne connaissons pas le nom de la classe interne privée et que nous supposons qu’elle possède un constructeur sans argument:

 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(Ssortingng[] 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); } } 

Maintenant, nous pouvons rendre ce code plus clair si nous avons des informations comme

  • nom de la classe interne,
  • arguments constructeur

Ainsi, au lieu de vérifier la liste des classes internes et de choisir la première, nous pouvons obtenir la classe interne sélectionnée par son nom en utilisant

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

De même, nous pouvons sélectionner le constructeur que nous voulons utiliser en getDeclaredConstructor(outerType,rest,of,parameter,types) donc si notre classe interne ressemblerait à

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

notre code pourrait être

 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, Ssortingng 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(Ssortingng[] args) throws Exception { Object innerClassObject = giveMeInnerInstance(); System.out.println(getFieldValue(innerClassObject, "x")); } } 

Sortie:

 inner object created 42