Est-il possible d'instancier une classe interne privée à partir d'une autre classe en utilisant Java réflexion. Par exemple, si j'ai pris ce code
public class Main {
public static void main(String[] args) {}
}
class OtherClass {
private class Test {}
}
est-il possible d'instancier et d'accéder à Test à partir de la méthode main dans la classe main.
Lorsque vous utilisez la réflexion, vous trouverez des constructeurs de cette classe interne prenant une instance de la classe externe comme argument supplémentaire (toujours le premier).
Consultez ces questions pour obtenir des informations connexes:
Comment puis-je instancier une classe membre par le biais d'une réflexion sur Android
En Java, comment accéder à la classe externe quand 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(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<InnerClass> 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 Java. Pour ce faire, vous devez avoir une instance de classe externe et invoquer le constructeur de classe interne qui utilisera l'instance de classe externe dans sa première 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 a 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(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);
}
}
Maintenant, nous pouvons rendre ce code plus clair si nous avons des informations comme
Ainsi, au lieu de vérifier la liste des classes internes et d'en choisir une 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 appelant 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, 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"));
}
}
Production:
inner object created
42