使用反射访问私有内部类字段

时间:2014-10-08 21:49:10

标签: java reflection

我想知道如何使用反射进行JUnit测试来访问私有内部类的私有字段。

这是我需要的一个例子:

public class A
{
    private Map<Integer, InnerClass> objectMap = new TreeMap<Integer, InnerClass>.descendingMap();

    private class InnerClass    
    {
        private int fieldOne
        private int fieldTwo
    }
}

在这个例子中,我想访问A实例中的fieldOne和fieldTwo的值。

我尝试了以下内容:

A instanceA = new A(); 

Field privateObjectMapField = A.class.getDeclaredField("objectMap");
privateObjectMapField.setAccessible(true);
NavigableMap<Integer, Class<?>> fieldValue =  (NavigableMap<Integer, Class<?>>) privateObjectMapField.get(instanceA);

// 5 is a key in the map so this should set declaredClass to an instance of InnerClass
Class<?> declaredClass = fieldValue.get(5).getClass();
Field fieldOne = declaredClass.getDeclaredField("fieldOne");
fieldOne.setAccessible(true);
Assert.assertEquals(100, fieldOne.getInt(declaredClass));

在这种情况下,它崩溃在:Class<?> declaredClass = fieldValue.get(5).getClass(); 错误:

java.lang.ClassCastException: com.test.A$InnerClass cannot be cast to java.lang.Class

有谁知道这里有什么问题?

我希望这个例子很清楚。

由于

2 个答案:

答案 0 :(得分:0)

应该是

NavigableMap<Integer, InnerClass> fieldValue = (NavigableMap<Integer, InnerClass>) privateObjectMapField.get(instanceA); 

答案 1 :(得分:0)

简答:

objectMapMap<Integer, A.InnerClass>,您将其视为Map<Integer, Class<?>>

试试这个,而不是:

NavigableMap<Integer, ?> fieldValue = ...

详细答案(如果您想了解泛型,请阅读并理解这一点)

fieldValue被声明为NavigableMap<Integer, Class<?>>,这意味着:

fieldValue.get(5)

只是转换为:

((Class<?>) fieldValue.get(5))

这是因为erasure的工作原理。基本上,declaredClass不是真的一个NavigableMap<Integer, Class<?>>,它只是一个NavigableMap(这是它的“原始类型”),编译器可以添加类型检查和自动铸造。

那么,如果键5上的对象实际上不是Class会发生什么?当你尝试强制转换它时,你会得到一个ClassCastException。这就是你发生的事情,因为第5项的对象是A.InnerClass,而不是Class。它根本没有什么不同:

Object o = new InnerClass();
Class<?> boom = (Class<?>) o; // ClassCastException

关于修复,这取决于你实际上要做什么。如果仅用于测试,您可以考虑使InnerClass具有默认可见性(也称为包私有)而非私有。您还可以将objectMap检索为Map<Integer, ?>,然后拨打fieldValue.get(5).getClass()。这将获得键5处的对象,恰好是InnerClass(但您不需要在编译时指定它),然后在其上调用Object.getClass()以获取它的班级。