我在第三方JAR中有一个设计很差的类,我需要访问它的一个私有字段。例如, 为什么我需要选择私人领域是必要的?
class IWasDesignedPoorly {
private Hashtable stuffIWant;
}
IWasDesignedPoorly obj = ...;
如何使用反射来获取stuffIWant的值?
我在第三方JAR中有一个设计很差的类,我需要访问它的一个私有字段。例如, 为什么我需要选择私人领域是必要的?
class IWasDesignedPoorly {
private Hashtable stuffIWant;
}
IWasDesignedPoorly obj = ...;
如何使用反射来获取stuffIWant的值?
当前回答
试试Apache common -lang3中的FieldUtils:
FieldUtils.readField(object, fieldName, true);
附注:在我看来,反思是邪恶的。
其他回答
使用Java中的反射,你可以访问一个类的所有私有/公共字段和方法到另一个类中。但是根据Oracle文档的章节缺陷,他们建议:
由于反射允许代码执行在非反射代码中不合法的操作,例如访问私有字段和方法,因此使用反射可能导致意想不到的副作用,这可能导致代码功能失调,并可能破坏可移植性。反射代码打破了抽象,因此可能会随着平台的升级而改变行为。”
下面是演示反射基本概念的代码片段
Reflection1.java
public class Reflection1{
private int i = 10;
public void methoda()
{
System.out.println("method1");
}
public void methodb()
{
System.out.println("method2");
}
public void methodc()
{
System.out.println("method3");
}
}
Reflection2.java
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflection2{
public static void main(String ar[]) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
Method[] mthd = Reflection1.class.getMethods(); // for axis the methods
Field[] fld = Reflection1.class.getDeclaredFields(); // for axis the fields
// Loop for get all the methods in class
for(Method mthd1:mthd)
{
System.out.println("method :"+mthd1.getName());
System.out.println("parametes :"+mthd1.getReturnType());
}
// Loop for get all the Field in class
for(Field fld1:fld)
{
fld1.setAccessible(true);
System.out.println("field :"+fld1.getName());
System.out.println("type :"+fld1.getType());
System.out.println("value :"+fld1.getInt(new Reflaction1()));
}
}
}
希望能有所帮助。
如果使用Spring:
在测试上下文中,ReflectionTestUtils提供了一些方便的工具,可以以最小的工作量帮助解决这个问题。它被描述为“用于单元和集成测试场景”。
在非测试上下文中,还有一个类似的类,名为ReflectionUtils,但它被描述为“仅供内部使用”——请参阅下面的回答,以更好地解释它的含义。
要解决原文中的例子:
Hashtable iWantThis = (Hashtable)ReflectionTestUtils.getField(obj, "stuffIWant");
为了访问私有字段,你需要从类声明的字段中获取它们,然后使它们可访问:
Field f = obj.getClass().getDeclaredField("stuffIWant"); //NoSuchFieldException
f.setAccessible(true);
Hashtable iWantThis = (Hashtable) f.get(obj); //IllegalAccessException
EDIT:正如aperkins注释的那样,访问字段、将其设置为可访问和检索值都可以抛出异常,尽管您需要注意的唯一检查异常是上面注释的。
如果您请求的字段名称与声明的字段不对应,则会抛出NoSuchFieldException。
obj.getClass().getDeclaredField("misspelled"); //will throw NoSuchFieldException
如果字段不可访问(例如,如果它是私有的,并且没有通过遗漏f.setAccessible(true)行进行访问,则抛出IllegalAccessException异常。
可能抛出的runtimeexception是securityexception(如果JVM的SecurityManager不允许你改变字段的可访问性),或者illegalargumentexception,如果你试图访问一个不属于字段类类型的对象上的字段:
f.get("BOB"); //will throw IllegalArgumentException, as String is of the wrong type
您可以使用jOOR来实现这一点。
class Foo {
private final String value = "ABC";
}
class Bar {
private final Foo foo = new Foo();
public String value() {
return org.joor.Reflect
.on(this.foo)
.field("value")
.get();
}
}
class BarTest {
@Test
void accessPrivateField() {
Assertions.assertEquals(new Bar().value(), "ABC");
}
}
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字段也是可取的。