使用Java反射,我可以访问私有静态嵌套类的私有字段吗?

时间:2014-01-15 08:55:31

标签: java reflection minecraft

我查看了我能找到的与Java反射相关的每个条目,似乎没有人能解决我正在处理的情况。我有一个带有静态嵌套类的类A,B类A还有一个字段,一个类型为B的数组,称为bArray。我需要从类外部访问此字段以及bArray元素的私有成员。我已经能够使用getDeclaredClasses获取静态嵌套类,我通常会使用getDeclaredFields和setAccessible获取私有字段bArray,但我似乎无法将它们放在一起以便能够从类外部迭代bArray。

这是我正在使用的示例类结构。

public class A {
    private A.B[] bArray = new A.B[16];

    private static class B {
        private int theX;
        private int theY;

        B(int x, int y) {
            this.theX = x;
            this.theY = y;
        }
        // More methods of A.B not included   
    }
}

我最终需要从A级以外的地方获取bArray及其字段theX和Y.

我不能使用像Class.forName(“classname”)这样的类的名称,因为代码都是通过混淆器运行的,并且名称都会更改,除了字符串中的名称。由于混淆,我可以使用字段顺序(索引)而不是名称。除了这个私有的静态嵌套数组字段之外,我已经设法找出了我需要的所有字段,方法和其他东西。

这是我尝试过的方向。这前三行似乎做了我想要的,但第四行说ABArray无法解决:

A myA = new A();
Class AB = stealNamedInnerClass(A.class);
Class ABArray = Array.newInstance(AB, 0).getClass();
ABArray myABArray = (ABArray)stealAndGetField(myA, ABArray);

这些是我引用的实用程序功能:

public static Field stealField(Class typeOfClass, Class typeOfField)
{
    Field[] fields = typeOfClass.getDeclaredFields();
    for (Field f : fields) {
        if (f.getType().equals(typeOfField)) {
            try {
                f.setAccessible(true);
                return f;
            } catch (Exception e) {
                break; // Throw the Exception
            }
        }
    }
    throw new RuntimeException("Couldn't steal Field of type \"" + typeOfField + "\" from class \"" + typeOfClass + "\" !");
}

public static Object stealAndGetField(Object object, Class typeOfField)
{
    Class typeOfObject;

    if (object instanceof Class) { // User asked for static field:
        typeOfObject = (Class)object;
        object = null;
    } else {
        typeOfObject = object.getClass();
    }

    try {
        Field f = stealField(typeOfObject, typeOfField);
        return f.get(object);
    } catch (Exception e) {
        throw new RuntimeException("Couldn't get Field of type \"" + typeOfField + "\" from object \"" + object + "\" !");
    }
}

public static Class stealNamedInnerClass(Class clazz)
{
    Class innerClazz = null;
    Class[] innerClazzes = clazz.getDeclaredClasses();
    if (innerClazzes != null) {
        for (Class inner : innerClazzes) {
            if (!inner.isAnonymousClass())
                return inner;
        }
    }
    return null;
}

2 个答案:

答案 0 :(得分:6)

是的,你可以简单地遍历你的bArray:

// First, get bArray
Class<A> aClass = A.class;
A instance = new A();
Class<?> bClass = aClass.getDeclaredClasses()[0];
Field field = aClass.getDeclaredField("bArray");
field.setAccessible(true);
Object[] bArray = (Object[]) field.get(instance);

// Then iterate and get field value
Field xField = bClass.getDeclaredField("theX");
xField.setAccessible(true);
Field yField = bClass.getDeclaredField("theY");
yField.setAccessible(true);
for (int i = 0; i < bArray.length; ++i) {
    Object bInstance = bArray[i];
    System.out.println("Item " + i + ": [x = " + xField.get(bInstance) + ", y = " + yField.get(bInstance) + "]");
}

在测试时,请确保您的数组已完全初始化(即包含非空值)。

答案 1 :(得分:2)

Class实例不是Java编程语言意义上的类型。它们只是物体。所以你做不到

Class ABArray = Array.newInstance(AB, 0).getClass();
ABArray myABArray = (ABArray)stealAndGetField(myA, ABArray);

因为ABArray只是Class类型的对象,而不是类型。如果它是一个类型,它无论如何都无济于事,因为您尝试使用的类型无法从该代码访问。您尝试访问的是private类,这是使用普通Java语言结构无法实现的。但您可以使用Object[],因为A.B[]Object[]的子类:

A myA = new A();
Class AB = stealNamedInnerClass(A.class);
Class ABArray = Array.newInstance(AB, 0).getClass();
Object[] myABArray = (Object[])stealAndGetField(myA, ABArray);

请注意,将元素作为Object处理是没有问题的,因为您将(并且可以)仅通过反射访问它们,并且反射方法无论如何都将Object作为输入。当然,您将失去编译时类型的安全性并仅在运行时检测错误,但这是不可避免的反射限制。