2009-08-06 53 views
7

我真的想喜歡仿製藥,但到目前爲止,他們已經勝過任何利益造成的麻煩。請告訴我我錯了。鑄造一個可比,然後比較

我理解增加@SuppressWarnings的必要性(「未登記」)使用無通用的框架時(春,休眠)。這一點確實降低了泛型的價值,正如要求將類傳遞給構造函數以避免刪除的缺陷一樣。然而,真正的刺總是似乎在鑄造。我通常會嘗試一段時間以獲得正確的語法,但是放棄純粹的嘗試,添加@SuppressWarnings,並繼續我的生活。

這裏有一個例子:我反映了一個bean來尋找兩個實例之間的差異。一些屬性實現Comparable,使得(a.equals(b)== false)但(a.compareTo(b)== 0)(例如BigDecimal,Date)。在這些情況下,我希望該財產被視爲相同。

MyObject original = getOriginal(); 
MyObject updated = getUpdated(); 
for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(MyObject.class)) { 
    // Assume I'm putting in the try/catch block 
    Object pOriginal = pd.getReadMethod().invoke(original, (Object[]) null); 
    Object pUpdated = pd.getReadMethod().invoke(updated, (Object[]) null); 

    boolean isPropertySame; 

    if (Comparable.class.isAssignableFrom(pOriginal.getClass())) { 
     // Type safety: The method compareTo(Object) belongs to the raw type Comparable. References to generic type Comparable<T> should be parameterized 
     isPropertySame = Comparable.class.cast(pOriginal).compareTo(Comparable.class.cast(pUpdated)) == 0; 

     // The method compareTo(capture#19-of ?) in the type Comparable<capture#19-of ?> is not applicable for the arguments (capture#21-of ? extends Comparable) 
     Comparable<?> comparable = Comparable.class.cast(pOriginal); 
     isPropertySame = comparable.compareTo(comparable.getClass().getTypeParameters()[0].getGenericDeclaration().cast(pUpdated)) == 0; 

     // Even if I get the generics right, I still get an error if pOriginal is java.sql.Timestamp and pUpdated is java.util.Date (happens all the time with Hibernate). 
     isPropertySame = (help); 

    } else { 
     isPropertySame = pOriginal.equals(pUpdated); 
    } 

    if (!isPropertySame) { 
     PropertyDelta delta = new PropertyDelta(pd, pOriginal, pUpdated); 
     dao.save(delta); 
    } 
} 

關於我可以投入(幫助)的任何想法?

+1

'Timestamp'和'Date'的問題是一個衆所周知的問題:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4631234。它已經在Java 6中修復(可能也在5u6)。 – notnoop 2009-08-06 22:00:54

+0

變量「originalValue」和「updatedValue」分別應該是「pOriginal」和「pUpdated」嗎? – erickson 2009-08-06 22:02:08

+0

我很高興Timestamp/Date混淆被修復。恐怕它不能幫助我,因爲我被困在1.5,但很高興知道未來。 – 2009-08-06 22:32:39

回答

0

好了,因爲我沒能找到一個「純粹」的方式來做到這一點,我一直運行到的角落的情況下(如,除了這一個事實,處理是集合性的難度),我決定讓我的增量生成方法方式 dumber。我意識到我只測試9種不同類型的對象,所以我可以測試我正在比較的9個對象中的哪一個,然後投射到該對象並進行特定於對象的測試。

以這種方式實現大約需要一個小時,即使每次任何對象發生變化時都必須重新編譯,但我認爲即使我花費了幾天的時間進行此維護,我仍然處於黑色狀態。

所以,最後,我想答案是沒有答案。 Java泛型以這樣一種方式實現,即不可能避免偶爾抑制編譯器警告並危及運行時類轉換異常。

4

這在我看來就像繞了艱辛的道路。你可以有你的bean實現媲美,在這種情況下,你只需要直接對它們進行比較,或者創建一個比較 -

public class Bean implements Comparable<Bean> {...} 

    public int compareTo(Bean other){ ... } 
} 

public int compare(Bean a, Bean b){ 
    Comparator<Bean> c = new Comparator<Bean>(){ 
    public int compareTo(Bean a, Bean b){ ... } 
    public boolean equals(Object o){.. } 
}; 
    return c.compare(a, b); 
} 

我同意你說的Java泛型可以得到位,呃......錯綜複雜。

+0

感謝您的幫助!我很抱歉,但我過分簡化了我的原始示例,以適應特定類別的比較工作。我現在更新了示例,以更準確地反映我收集每個已更改屬性的增量記錄的要求。您還可以想象,我需要深度掃描一個非常大的對象圖來生成很多這樣的delta記錄。 – 2009-08-06 21:06:15

2

我不很明白這有什麼錯只是簡單地做了以下內容:

MyObject original = getOriginal(); 
MyObject updated = getUpdated(); 
for (PropertyDescriptor pd : BeanUtils.getPropertyDescriptors(MyObject.class)) { 
    // Assume I'm putting in the try/catch block 
    Object pOriginal = pd.getReadMethod().invoke(original, (Object[]) null); 
    Object pUpdated = pd.getReadMethod().invoke(updated, (Object[]) null); 

    boolean isPropertySame; 
    if (pOriginal instanceof Comparable) { 
     @SuppressWarnings("unchecked") 
     Comparable<Object> originalValue = (Comparable<Object>) pOriginal; 
     @SuppressWarnings("unchecked") 
     Comparable<Object> updatedValue = (Comparable<Object>) pUpdated; 
     isPropertySame = originalValue.compareTo(updatedValue) == 0; 
    } else { 
     isPropertySame = pOriginal.equals(pUpdated); 
    } 

    if (!isPropertySame) { 
     PropertyDelta delta = new PropertyDelta(pd, pOriginal, pUpdated); 
     dao.save(delta); 
    } 
} 

在你的情況下使用Class.cast真的不類型安全幫助的。

+0

我試圖避免做到這一點,因爲它會引發一個警告:「可比較的是原始類型。參考泛型類型可比較的應該參數化」。現在,我可以在我的方法的頂部添加一個@SuppressWarnings,使其消失,但我希望得到一個「純粹」的答案。該解決方案完全避免了泛型。 儘管你對cast()的用法絕對正確。它與你的解決方案中的鑄造一樣脆弱。它存在的唯一原因是因爲我試圖執行運行時類型反射/操縱。 – 2009-08-06 22:03:53

+0

鑑於泛型在運行時被刪除,通用對你根本沒有幫助。我只是修改了代碼,使代碼更「通用」。編碼的可讀性比滿足編譯器更重要,因爲它不會給你帶來任何額外的類型安全性。 – notnoop 2009-08-06 22:34:42

+0

在運行時是不是可以內省類型參數(即可比較的中的「X」)?我以爲你可以做類似:similar.getClass()。getTypeParameters()[0] .getGenericDeclaration()。漂亮?當然不。鑑於此方法中的所有代碼都是用Java1.5編寫的,我認爲可以編寫一些不會引發任何警告/錯誤的內容,並且是類型安全的,而不必禁用任何內容。這是一個夢想嗎? – 2009-08-09 03:18:53

1

它看起來像假設是,如果一個類實現Comparable,類型參數是類本身。那就是「class X implements Comparable<X>」。如果是這樣的話,那麼它是有道理的說,

X a = new X(1), b = new X(2); 
a.compareTo(b); 

但是,這絕對是可以定義,如「class X implements Comparable<Y>」一類。然後人們可以嘗試這樣的東西......

X a = new X(1), b = new X(2); 
a.compareTo((Y) b); 

&hellip;但很明顯,將會產生ClassCastException,因爲b不是Y的實例。

所以,警告是有效的。使用原始類型的代碼不是類型安全的,並且可能在運行時引發異常。

+0

這裏的問題是,直到運行時才知道Bean屬性類型。您無法以任何方便的方式知道「Comparable」的類型參數。 – notnoop 2009-08-07 01:47:57

+0

@erickson我完全同意你的分析。但是,不應該有辦法在運行時實現正確的轉換 - 以便我可以測試兩個對象是否可以相互比較,然後執行比較?鑑於所有可用的運行時方法,似乎你應該可以做到這一點...... – 2009-08-09 03:22:42