2012-02-13 50 views
4

所以我有一個功能,看起來像這樣:處理來自java「未經檢查的轉換」的潛在運行時異常的最佳方式是什麼?

@SuppressWarnings("unchecked") 
public static <E> Set<E> getSetOfClass(Query q,Class<E> clazz) { 
    return new LinkedHashSet<E>(q.getResultList()); 
} 

我相信這是什麼東西做的是一個javax.persistence.Query並返回其結果集作爲通用Set<Class>

這似乎是一個很好的解決方案,但首先,它實際上是在做我認爲的事情,這是實現這一目標的最佳方式嗎?我覺得很奇怪,我從來沒有引用我的clazz param,但它似乎做我想做的。其次,如果這一切都是正確和明智的,那麼這會拋出什麼錯誤?我想象如果我給它一個不正確的類,這個函數將不起作用,儘管我不確定這一點。

如果我做這樣的事情:

Query q = em.createQuery("FROM Element"); 
Set<Fish> s = MyUtil.getSetOfClass(q, Fish.class); 

如果Fish不是Element那麼會發生什麼超?我應該假設這個函數總是被正確使用,或者我應該這樣做錯誤處理嗎?人們對最佳實踐方法的建議是什麼?

問候,

格倫X

+1

關於未使用'clazz',其實,你不使用你的類PARAM因爲你無需再通過。您可以忽略該參數並將該方法調用爲: 'MyUtil。 getSetOfClass(q)',但如果你不習慣它,它就是一個更偉大的語法。我建議使用@Andreas_D推薦的'clazz'參數。 – Blaisorblade 2012-02-13 15:41:32

回答

3

getSetOfClass不保證該集合中的所有元素都E類型的對象。如果你把它錯誤地(你總是能),如:

Set<Cat> cats = getSetOfClass(findAllFish(), Cat.class); 

您會收到在各個地方以後造型異常......

我一些檢查,增加了公衆getSetOfClass方法以保證集的內容類型匹配:

@SuppressWarnings("unchecked") 
public static <E> Set<E> getSetOfClass(Query q,Class<E> clazz) { 
    return getSetOfClass(Query q,Class<E> clazz, true); 
} 

@SuppressWarnings("unchecked") 
public static <E> Set<E> getSetOfClass(Query q,Class<E> clazz, boolean validate) { 
    List result = q.getResultList(); 
    if (validate) { 
     for (Object o:result) { 
      if(!o.getClass().equals(clazz)) { 
       // handle error 
      } 
     } 
    } 
    return new LinkedHashSet<E>(result); 
} 
+1

也許'Class.isAssignableFrom(Class)'比Object.equals(Object)'更合適。 – 2012-02-13 14:27:47

+0

是的,會的。取決於要求 - 「模式」適用於兩種測試。 (我通常會避免推薦'isAssignable'時,我沒有一個IDE來測試它 - 有50%的機會在錯誤的方向上使用它;)) – 2012-02-13 14:30:22

+0

我遇到完全相同的問題:) – 2012-02-13 14:39:20

2

要添加到@ Andreas_D的回答,只記得所有的Java泛型信息只用於在編譯時檢查您的類型正確性代碼,在運行時被擦除。因此,你會得到有效的是這樣的:

public static Set<Object> getSetOfClass(Query q,Class<Object> clazz) { 
    return new LinkedHashSet<Object>(q.getResultList()); 
} 

在運行時一切都將只是工作,至於上面的方法去這意味着。
更新:正如@Blaisorblade指出的那樣,getSetOfClass方法可以使用clazz來檢查類型是否正確,如果類型錯誤,則可以快速失敗。雖然在編譯時無法完成,但它可以更容易地在運行時發生故障時查明問題。

現在假設以後你:

Query q = em.createQuery("FROM Element"); 
Set<Fish> s = MyUtil.getSetOfClass(q, Fish.class); 
for(Fish fish : s){ 
    fish.swim(); 
} 

然後在運行時它看起來像:

Query q = em.createQuery("FROM Element"); 
Set<Object> s = MyUtil.getSetOfClass(q, Fish.class); 
for(Object fish : s){ 
    ((Fish)fish).swim(); 
} 

現在你可以看到,如果元素Cat類型會發生什麼。(Fish)fish部分將拋出ClassCastException(如果它得到那麼多)。因此

泛型是真正有用的,當類型信息可以通過編譯器不從開始結束任何unchecked警告進行跟蹤。對於泛型在中間被「黑客入侵」的其他案例(如您的),它不能保證您的程序正確性。這是不可避免的,特別是在數據被保存到磁盤或數據庫的情況下,因爲無法確定持久數據的類型是否正確。程序員必須小心。

+0

你的帖子很有趣,我會贊成它,除了在這種情況下,因爲(很奇怪)一個'Class'實例通過,你可以在運行時檢查一切正常。當然,正如你所說的那樣,編譯器不能檢查它:你需要正確地取得未經檢查的代碼。如果你在你的結論中加入這個,我會贊成它。 – Blaisorblade 2012-02-13 15:37:56

+0

完成,謝謝你的建議。 – rodion 2012-02-14 01:33:29

1

我從來沒有肯定我是否喜歡仿製藥與否。在這種情況下,他們似乎是個好主意,並且會爲您節省很多麻煩。由於持久性似乎還不支持它們,所以我會穿上我的反泛型帽子並解釋如何完成Java編程。

我的建議,那麼,將放棄對仿製藥和類,就回到一個普通的老套裝:如果可以的話,

public static Set getSetOfClass(Query q) { 
    return new LinkedHashSet(q.getResultList()); 
} 

(使用@SuppressWarnings(「未登記」),根據需要」噸得到1.4編譯器。)

如果查詢只含有鄂氏,你將永遠不會有問題。無論如何,這種麻煩在運行時很少見,很明顯,並且最好由錯誤使用方法的程序員處理。沒有什麼說「改變你的基本方法」,就像在運行時意外的ClassCastException一樣。

如果有偶爾的,合法的,非-E魚在那裏的對象,誰正在使用的方法的程序員更好地比你來對付他們。他們可以在運行時檢查,也可以選擇扔掉單個魚或將整個套裝扔掉,以適合他們的目的,他們知道而不是。

如果你知道他們的目的,那麼你也許可以通過添加Clazz參數來爲他們節省一些麻煩,以便你知道他們想要什麼。然後,您可以執行過濾或返回null或拋出自己的已檢查異常,或者返回一個特殊的類對象,詳細解釋Set內容的性質。但要確保你不會做比保存方法用戶更多的工作。

+0

我提供的服務,這些查詢是在一個DAO和服務提供了各種'Set getSet()'方法,所以在某些時候我必須做這些轉換。我已經選擇將它隔離到單個實用程序類中,我可以在一個地方禁止任何警告,並讓其他人對其泛型感到滿意。我認爲這是一個相當乾淨的方式來做到這一點。 – Link19 2012-02-13 15:45:20

相關問題