简体   繁体   English

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

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

I have data model classes that contain private fields which are meant to be read-only (via a getter function). 我有数据模型类,包含私有字段,这些字段是只读的(通过getter函数)。 These fields are set by my JPA persistence provider (eclipselink) during normal operation, using the contents of the database. 这些字段由我的JPA持久性提供程序(eclipselink)在正常操作期间使用数据库的内容设置。 For unit tests, I want to set them to fake values from a mockup of the persistence layer. 对于单元测试,我想将它们设置为来自持久层的模型的伪值。 How can I do that? 我怎样才能做到这一点? How does eclipselink set these values, anyway? 无论如何,eclipselink如何设置这些值?

Simplified example: 简化示例:

@Entity
class MyEntity
{
    @Id
    private Integer _ix;

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

Can you just Mock the Entity itself, providing your own implemenations of the getters? 你可以嘲笑实体本身,提供你自己的getter实现吗?

You could create an anonymous extension in your mock persistence layer: 您可以在模拟持久层中创建匿名扩展:

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

You need to use the Reflection API. 您需要使用Reflection API。 Use Class.getField() to get the field, then call setAccessable(true) on that field so that you may write to it, even though it is private, and finally you may call set() on it to write a new value. 使用Class.getField()来获取该字段,然后在该字段上调用setAccessable(true),以便您可以写入它,即使它是私有的,最后您可以在其上调用set()来写入新值。

For example: 例如:

public class A {
    private int i;
}

You want to set the field 'i' to 3, even though it is private: 您希望将字段“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);
}

There are a number of exceptions that you will need to handle. 您需要处理许多例外情况。

You can use a test library like Mockito to access objects internal state in read and write mode. 您可以使用像Mockito这样的测试库来以读写模式访问对象内部状态。 For example with Mockito use: 例如,使用Mockito:

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

You can use a mocking framework like powermock to by pass encapsulation. 你可以通过封装来使用像powermock这样的模拟框架。 In powermock you'd use Whitebox.setInternalState(..) to set a private member. 在powermock中,您将使用Whitebox.setInternalState(..)来设置私有成员。

A less invasive method would be to mock the getter method. 一种侵入性较小的方法是模拟getter方法。 Whether this is feasible would depend on what else depends on the internal state but if it is enough, it's the cleaner solution. 这是否可行将取决于其他什么取决于内部状态,但如果它足够,它是更清洁的解决方案。

Some methods I've used in the past: 我过去使用的一些方法:

  • Make _ix protected, create a subclass where you implement a setter 使_ix受保护,创建一个实现setter的子类
  • Make a constructor taking the value for _ix as a parameter 创建一个构造函数,将_ix的值作为参数
  • Use reflection 使用反射

Another option, if you really hate to make things public, is to create a subclass for testing, and provide public access there. 另一种选择,如果你真的讨厌公开,就是创建一个用于测试的子类,并在那里提供公共访问。

You have a few options: 你有几个选择:

  • Create stubs to replace your entity (extract an interface first) 创建存根以替换您的实体(首先提取接口)
  • Use Reflection 使用反射
  • Add a public setter for testing 添加公共setter进行测试
  • Keep your tests within the package and use a default scope 将测试保留在包中并使用默认范围

For a bunch of useful techniques, have a look at Michael Feather's book, Working Effectively With Legacy Code 有关一系列有用的技巧,请查看Michael Feather的书“有效地使用遗留代码”

You can add constructor with parameter for your read-only variable. 您可以为只读变量添加带参数的构造函数。 Don't forget to add a default (zero parameter) constructor. 不要忘记添加默认(零参数)构造函数。

@Entity
class MyEntity
{
    @Id
    private Integer _ix;

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

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

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

The constructor is a best way I think. 构造函数是我认为最好的方法。 If this entity has to be really readonly (not allowed to create new instances in production code at all) you can make constructor with package access and use it only within the tests. 如果此实体必须非常只读(根本不允许在生产代码中创建新实例),则可以使用包访问权限构建器,并仅在测试中使用它。 And there is a possibility that even if you make your default constructor private or with package access, your persistance provider still be able to work with such entity, but not sure though - check with eclipselink docs. 并且即使您将默认构造函数设为私有或具有包访问权限,您的持久性提供程序仍然可以使用此类实体,但不确定 - 请查看eclipselink文档。

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

相关问题 如何访问匿名内部类中容器类的私有类成员? - How can I access private class members of container class within the anonymouse inner class? 为什么外部 Java 类可以访问内部类私有成员? - Why can outer Java classes access inner class private members? 为什么我可以访问封闭类引用的私有成员 - Why can I access the private members of an enclosing class reference 如何使用main()中的对象数组访问类私有成员? - How can I access class private members using array of objects in main()? 如何使用Java访问类的成员 - How to access members of a class in Java Spring / Hibernate如何访问私有成员? - How Can Spring/Hibernate Access Private Members? 当类本身的对象不能访问时,该关键字如何访问类的私有成员 - How can this keyword access private members of class while object of class itself can not 如何从Java中的静态内部类访问外部类的私有成员 - How to access private members of outer class from static inner class in Java 在更高版本的 Java 中,内部类如何访问封闭类的私有成员? - How do inner class access enclosing class' private members in higher versions of Java? 在Java嵌套类中,封闭类可以访问内部类的私有成员吗? - In Java nested classes, can the enclosing class access private members of inner classes?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM