我在第三方JAR中有一个设计很差的类,我需要访问它的一个私有字段。例如, 为什么我需要选择私人领域是必要的?

class IWasDesignedPoorly {
    private Hashtable stuffIWant;
}

IWasDesignedPoorly obj = ...;

如何使用反射来获取stuffIWant的值?


当前回答

尝试绕过这种情况下的问题,您想要设置/获取数据的类是您自己的类之一。

只需为此创建一个公共setter(Field f, Object value)和公共Object getter(Field f)。您甚至可以在这些成员函数中自己进行一些安全检查。例如,对于setter:

class myClassName {
    private String aString;

    public set(Field field, Object value) {
        // (A) do some checkings here  for security

        // (B) set the value
        field.set(this, value);
    }
}

当然,现在你必须在设置字段值之前为sString找到java.lang.reflect.Field。

我确实在一个通用的resultset -to-and-from-model- mapping器中使用了这种技术。

其他回答

还有一个没有提到的选项:使用Groovy。Groovy允许您访问私有实例变量,这是该语言设计的一个副作用。无论是否有字段的getter,都可以使用

def obj = new IWasDesignedPoorly()
def hashTable = obj.getStuffIWant()

Java 9引入了变量句柄。您可以使用它们访问类的私有字段。

示例代码如下所示:

var lookup = MethodHandles.lookup();
var handle = MethodHandles
    .privateLookupIn(IWasDesignedPoorly.class, lookup)
    .findVarHandle(IWasDesignedPoorly.class, "stuffIWant", Hashtable.class);
var value = handle.get(obj);

使用Lookup和VarHandle对象作为静态final字段也是可取的。

正如oxbow_lakes提到的,您可以使用反射来绕过访问限制(假设您的SecurityManager允许您)。

也就是说,如果这门课设计得如此糟糕,以至于你不得不求助于这样的伎俩,也许你应该寻找另一种选择。当然,这个小技巧现在可能会为你节省几个小时,但它会让你付出多少代价呢?

您需要完成以下操作:

private static Field getField(Class<?> cls, String fieldName) {
    for (Class<?> c = cls; c != null; c = c.getSuperclass()) {
        try {
            final Field field = c.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field;
        } catch (final NoSuchFieldException e) {
            // Try parent
        } catch (Exception e) {
            throw new IllegalArgumentException(
                    "Cannot access field " + cls.getName() + "." + fieldName, e);
        }
    }
    throw new IllegalArgumentException(
            "Cannot find field " + cls.getName() + "." + fieldName);
}

试试Apache common -lang3中的FieldUtils:

FieldUtils.readField(object, fieldName, true);

附注:在我看来,反思是邪恶的。