簡體   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