我在第三方JAR中有一个设计很差的类,我需要访问它的一个私有字段。例如, 为什么我需要选择私人领域是必要的?
class IWasDesignedPoorly {
private Hashtable stuffIWant;
}
IWasDesignedPoorly obj = ...;
如何使用反射来获取stuffIWant的值?
我在第三方JAR中有一个设计很差的类,我需要访问它的一个私有字段。例如, 为什么我需要选择私人领域是必要的?
class IWasDesignedPoorly {
private Hashtable stuffIWant;
}
IWasDesignedPoorly obj = ...;
如何使用反射来获取stuffIWant的值?
当前回答
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字段也是可取的。
其他回答
还有一个没有提到的选项:使用Groovy。Groovy允许您访问私有实例变量,这是该语言设计的一个副作用。无论是否有字段的getter,都可以使用
def obj = new IWasDesignedPoorly()
def hashTable = obj.getStuffIWant()
如果使用Spring:
在测试上下文中,ReflectionTestUtils提供了一些方便的工具,可以以最小的工作量帮助解决这个问题。它被描述为“用于单元和集成测试场景”。
在非测试上下文中,还有一个类似的类,名为ReflectionUtils,但它被描述为“仅供内部使用”——请参阅下面的回答,以更好地解释它的含义。
要解决原文中的例子:
Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");
使用XrayInterface工具非常简单。只需定义缺失的getter /setter,例如:
interface BetterDesigned {
Hashtable getStuffIWant(); //is mapped by convention to stuffIWant
}
用x射线照射你设计糟糕的项目:
IWasDesignedPoorly obj = new IWasDesignedPoorly();
BetterDesigned better = ...;
System.out.println(better.getStuffIWant());
在内部,这依赖于反思。
反射不是解决问题的唯一方法(它是访问类/组件的私有功能/行为)
另一种解决方案是从.jar中提取类,使用(比如)Jode或Jad反编译它,更改字段(或添加一个访问器),然后根据原始的.jar重新编译它。然后在类路径中将新的.class放在.jar的前面,或者将其重新插入到.jar中。(jar工具允许你提取和重新插入到一个现有的。jar)
如下所述,这解决了访问/更改私有状态的更广泛问题,而不是简单地访问/更改字段。
当然,这要求.jar不能被签名。
尝试绕过这种情况下的问题,您想要设置/获取数据的类是您自己的类之一。
只需为此创建一个公共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器中使用了这种技术。