2010-04-28 53 views
5

我有一個對象,它具有很多屬性,每個屬性都有它的getter和setter。每個屬性都有一個非原始類型,我不知道在運行時。使用反射將一個值設置到對象中

例如,我有什麼是這樣的:

public class a{ 

    private typeA attr1; 
    private typeB attr2; 

    public typeA getAttr1(){ return attr1; } 
    public typeB getAttr2(){ return attr2; } 

    public void setAttr1(typeA at){ attr1 = at; } 
    public void setAttr2(typeB at){ attr2 = at; } 
} 

public class typeA{ 
    public typeA(){ 
     // doesn't matter 
    } 
} 

public class typeB{ 
    public typeB(){ 
     // doesn't matter 
    } 
} 

因此,使用反射,我所獲得的設置器方法的一個屬性。以標準方式設置一個值是這樣的:

a test = new a(); 
a.setAttr1(new typeA()); 

但是我怎樣才能使用反射?我已經得到了setAttr1()方法使用反射,但我不知道如何創建一個新的typeA對象將被插入設置。

回答

11

使用Class#newInstance()

Class<TypeA> cls = TypeA.class; 
TypeA typeA = cls.newInstance(); 

或者,在特定的情況下,你必須確定方法參數的類型:

Class<?> cls = setterMethod.getParameterTypes()[0]; 
Object value = cls.newInstance(); 
setterMethod.invoke(bean, value); 

您可以瞭解更多關於反思Sun tutorial on the subject。也就是說,類名ought以大寫字母開頭。我在上面的例子中糾正了它。順便說一句,與其重新創建Javabean反射輪,您可能會發現其中一個工具here也很有用。

+2

它們必須有一個默認的(無參數)構造函數(+1) – Bozho 2010-04-28 19:53:29

1

如果你想設置一個「新鮮」的對象在你的類的每一個二傳手,你通常可以通過獲取Method做到這一點,對於每一個方法,你得到的參數與getParameterTypes()Class,爲每個類調用Class.newInstance() ...並交叉你的手指(這應該與原始類型打破 - 我懷疑Java在這裏自動裝箱)。 您可以隨時問,如果一個參數是pimitive調用isPrimitive()

你爲什麼要設置一類原始的領域「空」的情況?它們已經初始化。你想「重置」它們嗎?

+0

您可以使用int.class,boolean.class等搜索使用原語的方法或Integer.TYPE ... – pyb 2015-01-23 18:51:43

3

Class對象中使用getDeclaredFields()方法獲取所有字段,然後使用field.set(classInstance, value)設置實例中字段的值。注意:如果該字段是私人的,您可能必須將字段上的可訪問標誌設置爲true。不需要依賴setter方法。

+2

這是不好的建議,因爲你繞過了制定者。即使現在並不重要,它可能遲一點,因爲這是反思,沒有人會知道。 – Yishai 2010-04-28 21:03:33

+1

好吧,反思已經是黑魔法,整點是讓你打破書中的每一條規則。如果他正在構建某種注入機制,那麼沒有理由不直接注入,J2EE會這樣做(@PersistenceContext) – 2010-04-28 22:01:16

2

我在做某些事情時遇到過這個問題。我的一般結論是,每當我覺得我需要一個領域的班級,我做錯了。這是我的思維過程:

問題: - 我需要大量的字段來保存這些數據 - 所有這些領域需要的樣板鉅額

解決方案:

  • 使用反射來降低樣板<「你在這裏」
  • 使用元數據指定字段應如何使用

新問題:

  • 反思是很難理解,當有人以全新的面貌在代碼
  • 一旦你足夠的元,以消除更多的樣板,該領域往往在代碼中沒有提及,除非通過metadata-他們爲什麼是田地?
  • 指定在碼元數據變成龐大的相當快(最簡單的方式是一個字符串數組,順便)

解決方案:啓動一個集合中的存儲數據和在外部數據文件指定元數據

新問題:錯誤很難找到

請謹慎對待錯誤檢查,並非常明確地表明您的錯誤消息。確保其他可能使用您的代碼的程序員閱讀錯誤消息。嘗試指示元數據何時缺失或錯誤以及程序員應如何更新metdata - 包括元數據文件的位置。

問題:無類型安全

呀,這成了時代有些煩人。我最終在元數據中包含了類型信息,這樣如果有人在字段中輸入了錯誤的值,可能會發現它 - 從本質上講,這會將類型安全從構建時間移動到運行時間,這對我來說是很好的。

問題:重複所需的元數據在整個對象的生命

而不是每次它的使用時間尋找它的名字,我會解析開頭的元數據,並把它放在一個對象(電話它是一個IntHolder)。這個持有者最終將在散列表中,它將包含當前值以及對解析元數據的引用。

這裏是我的元數據最終將用於樣式表的一個字段:

FieldName:  Age 
FieldType  Integer 
ScreenBinding: AgeTextField 
DBBinding:  User.age 
Validation: IntRange(0, 120); "Age is out of range" 

字段名稱可能是如何顯示給用戶,或者只是在你的程序中使用。一般來說,你不需要直接操縱這種類型的數據名稱 - 但當然你有時會這樣做。

當你確實需要使用時,使用getInt(「Age」)和setInt(「Age」,12)而不是getAge()和setAge(12) - 稍微更冗長,但並不是真正的問題。

如果這是一個問題,你可以使getAge/setAge輔助方法,你永遠不需要知道它不是一個字段,但是它不會再次在樣板上堆積。

FieldType:指定它的存儲方式,可以實現類型檢查。

ScreenBinding和DBBinding用於將值複製到其他系統和從其他系統中複製出來。我也使用這種機制將數據從服務器傳輸到客戶端並返回。

有趣的是驗證。當從屏幕上提取數據時,它可以以非常有計劃的方式傳遞給驗證器。提交到數據庫之前可以使用相同的驗證器。

驗證器可以做更多的事情,它們可以充當觸發器(如果值更改,執行此操作)或操作(當用戶提交屏幕時,執行此操作)。這些是一個簡單的對象,能夠通過接口獲取值 - 它們可以像您喜歡的那樣靈活或強大,但除了通過元數據外,不會直接綁定到任何對象。

這種方法的唯一問題是你必須是喜歡書寫夾具而不是容易出錯的樣板的程序員。 (是的,我發現時間大致相等,但當我不得不實施樣板時,我傾向於變得非常慢)

經過這樣的幾次我真的很喜歡這個模式,但它很難實現。下次我這樣做時,我會嘗試將它變成某種類型的庫。