通过Java反射技术,能否从另一个类中实例化一个私有内部类。例如,如果我有以下代码:
public class Main {
public static void main(String[] args) {}
}
class OtherClass {
private class Test {}
}
在main方法中实例化并访问Test,这是否可能?
通过Java反射技术,能否从另一个类中实例化一个私有内部类。例如,如果我有以下代码:
public class Main {
public static void main(String[] args) {}
}
class OtherClass {
private class Test {}
}
在main方法中实例化并访问Test,这是否可能?
是的,您可以使用Java反射实例化私有内部类。要做到这一点,您需要先获取外部类的实例,并调用内部类构造函数,该构造函数将在其第一个参数中使用外部类实例。
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");
}
}
}
当我们不知道私有内部类的名称并假设它具有无参构造函数时:
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);
}
}
如果我们有以下信息:
我们现在可以使这段代码更加清晰明了。
因此,我们可以通过名称获取所选的内部类,而不是检查内部类列表并选择第一个。
Class<?> inner = Class.forName("our.pack.age.OuterClass$InnerClass")
// ^^^^^^^^^^^
我们同样可以通过调用getDeclaredConstructor(outerType,rest,of,parameter,types)
来选择我们想要使用的构造函数,因此如果我们的内部类看起来像这样
class OuterClass {
private class InnerClass {
private int x;
public InnerClass(int x) {
this.x = x;
System.out.println("inner object created");
}
}
}
我们的代码可能需要改进
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"));
}
}
输出:
inner object created
42
setAccessible(true)
。 - Pshemo使用反射时,您会发现内部类的构造函数需要一个外部类实例作为附加参数(始终是第一个)。
有关相关信息,请参阅以下问题:
示例:
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();
}
}
}
OuterClass.InnerClass.class
只能在 OuterClass
内部使用。由于 InnerClass
是私有的,因此我们无法在外部使用 InnerClass.class
部分,而且很可能我们的代码不会成为 OuterClass
的一部分(如果是的话,就不需要反射)。 - Pshemoouter.new InnerClass();
在这里确实可以做到。当从外部使用反射访问内部类时,可能希望它实现一个可访问的接口,以便通过反射调用各个方法时避免痛苦。然而,如果这一切都是在自己的代码中完成的(而不是为了解决第三方库等问题),那么这可能是一个设计缺陷 - 因此,根本不可取。与这个例子类似,如果外部类不是final的话,我们也可以从同一个包或者外部类的子类中“窃取”一个protected成员。 - Sampublic static void main(String[] args) throws Exception {
// to get first class in OtherClass
Class<?> innerClass = OtherClass.class.getDeclaredClasses()[0];
// getDeclaredConstructors for private constructor
Constructor<?> constructor = innerClass.getDeclaredConstructors()[0];
// to enable accessing private constructor
constructor.setAccessible(true);
OtherClass outerObject = new OtherClass();
//// create instance of Test by reflection
Object o = constructor.newInstance(outerObject);
System.out.println(o);
}
Inner
类(非静态嵌套类),您需要拥有外部类的实例。要创建一个Static Nested
类的实例,您不需要外部类的实例。 - Ray