我想知道如何使用反射进行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
有谁知道这里有什么问题?
我希望这个例子很清楚。
由于
答案 0 :(得分:0)
应该是
NavigableMap<Integer, InnerClass> fieldValue = (NavigableMap<Integer, InnerClass>) privateObjectMapField.get(instanceA);
答案 1 :(得分:0)
简答:
objectMap
是Map<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()
以获取它的班级。