繁体   English   中英

如何在Java中访问私有类成员?

[英]How can I access private class members in Java?

我有数据模型类,包含私有字段,这些字段是只读的(通过getter函数)。 这些字段由我的JPA持久性提供程序(eclipselink)在正常操作期间使用数据库的内容设置。 对于单元测试,我想将它们设置为来自持久层的模型的伪值。 我怎样才能做到这一点? 无论如何,eclipselink如何设置这些值?

简化示例:

@Entity
class MyEntity
{
    @Id
    private Integer _ix;

    public Integer ixGet()
    {
        return this._ix;
    }
}

你可以嘲笑实体本身,提供你自己的getter实现吗?

您可以在模拟持久层中创建匿名扩展:

MyEntity x = new MyEntity() {
    public Integer ixGet() { return new Integer(88); }
};

您需要使用Reflection API。 使用Class.getField()来获取该字段,然后在该字段上调用setAccessable(true),以便您可以写入它,即使它是私有的,最后您可以在其上调用set()来写入新值。

例如:

public class A {
    private int i;
}

您希望将字段“i”设置为3,即使它是私有的:

void forceSetInt(Object o, String fieldName, int value) {
    Class<?> clazz = o.getClass();
    Field field = clazz.getDeclaredField(fieldName);
    field.setAccessible(true);
    field.set(o, value);
}

您需要处理许多例外情况。

您可以使用像Mockito这样的测试库来以读写模式访问对象内部状态。 例如,使用Mockito:

//read
Integer i = Whitebox.getInternalState(myEntity,"_ix")
//Write 
Whitebox.setInternalState(myEntity,"_ix", 123) 

你可以通过封装来使用像powermock这样的模拟框架。 在powermock中,您将使用Whitebox.setInternalState(..)来设置私有成员。

一种侵入性较小的方法是模拟getter方法。 这是否可行将取决于其他什么取决于内部状态,但如果它足够,它是更清洁的解决方案。

我过去使用的一些方法:

  • 使_ix受保护,创建一个实现setter的子类
  • 创建一个构造函数,将_ix的值作为参数
  • 使用反射

另一种选择,如果你真的讨厌公开,就是创建一个用于测试的子类,并在那里提供公共访问。

你有几个选择:

  • 创建存根以替换您的实体(首先提取接口)
  • 使用反射
  • 添加公共setter进行测试
  • 将测试保留在包中并使用默认范围

有关一系列有用的技巧,请查看Michael Feather的书“有效地使用遗留代码”

您可以为只读变量添加带参数的构造函数。 不要忘记添加默认(零参数)构造函数。

@Entity
class MyEntity
{
    @Id
    private Integer _ix;

    public MyEntity(Integer ix) {
        _ix = ix;
    }

    public MyEntity() {
        /*
         * Default constructor
         */
    }

    public Integer ixGet()
    {
        return this._ix;
    }
}

构造函数是我认为最好的方法。 如果此实体必须非常只读(根本不允许在生产代码中创建新实例),则可以使用包访问权限构建器,并仅在测试中使用它。 并且即使您将默认构造函数设为私有或具有包访问权限,您的持久性提供程序仍然可以使用此类实体,但不确定 - 请查看eclipselink文档。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM