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

class IWasDesignedPoorly {
    private Hashtable stuffIWant;
}

IWasDesignedPoorly 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());

在内部,这依赖于反思。

其他回答

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

只需为此创建一个公共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器中使用了这种技术。

使用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());

在内部,这依赖于反思。

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字段也是可取的。

使用Soot Java Optimization框架直接修改字节码。 http://www.sable.mcgill.ca/soot/

Soot完全是用Java编写的,并且适用于新的Java版本。

为了访问私有字段,你需要从类声明的字段中获取它们,然后使它们可访问:

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