Using Java reflection, can I access a private fiel

2019-08-31 02:11发布

I've looked through every entry related to Java reflection I can find and none seem to address the situation I'm dealing with. I have a class A with a static nested class in it, B. Class A also has as one of its fields, an array of type B, called bArray. I need to access this field from outside the class as well as the private members of the elements of bArray. I have been able to get the static nested class using getDeclaredClasses and I would normally get the private field bArray with getDeclaredFields and setAccessible but I can't seem to put it all together to be able to iterate through bArray from outside the class.

Here is an example class structure that I'm working with.

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   
    }
}

I ultimately need to get at bArray and its fields theX and theY from outside of class A.

I cannot use the names of the classes like Class.forName("classname") because the code all gets run through an obfuscator and the names all change except the ones in strings of course. I can use the field order (index) but not the name due to obfuscation. I've managed to pick out all of the fields, methods and whatnot that I need except this private static nested array field.

This is the direction I've tried. The first 3 lines of this seem to do what I want but the 4th says ABArray cannot be resolved:

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

These are the utility functions I'm referencing:

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条回答
Root(大扎)
2楼-- · 2019-08-31 02:47

Class instances are not types in the sense of the Java programming language. They are just objects. So you can’t do

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

because ABArray is just an object of type Class, not a type. If it were a type, it wouldn’t help anyway as the type you are trying to use is not accessible from that code. Which is what your attempt is all about, you are trying to access a private class which is not possible using normal Java language constructs. But you can just use Object[] as A.B[] is a subclass of Object[]:

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

Note that processing the elements as Object is no problem as you will (and can) access them via reflection only and the reflection methods take Object as input anyway. Of course, you will lose compile-time type safety and detect mistakes at runtime only but that’s an unavoidable limitation of reflection.

查看更多
对你真心纯属浪费
3楼-- · 2019-08-31 02:54

Yes, you can simply iterate over your 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) + "]");
}

When you are testing it, make sure your array is fully initialized (i.e. contains non-null values).

查看更多
登录 后发表回答