2011-07-09 198 views
3

Java集合接口(例如,ListSet)定義了接受任何對象的contains方法。檢查泛型類型

public boolean contains(Object o) 

然而,當涉及到實施這種方法,我工作特別的收藏需要,我有一種類型是與類的泛型類型E(即或者是E級,或兼容E的子類,或者如果E是接口,則實現E的類)。換句話說,如果o可鑄造成E型,那麼它是兼容的。這就提出一個問題,因爲Java擦除泛型類型的信息,所以這樣的事情是不可能的:

public boolean contains(Object o) 
{ 
    if(o instanceof E) // compile error due to type erasures 
    { 
     // ... check if this collection contains o 
    } 
    return false; 
} 

我的問題是什麼是完成這樣的事情的最好方法? Oracle Article on Type Erasures提到這是禁止的,但不提供任何解決此問題的方法。

我只能想到一個半優雅的方式來解決這個問題:

做強制轉換爲類型E.如果轉換失敗,鄰類型不能爲E或類型的子類E.

public boolean contains(Object o) 
{ 
    try 
    { 
     E key = (E) o; // I know it's unsafe, but if o is castable to E then the method should work 
     // check if this collection contains key 
    } 
    catch(ClassCastException e) 
    { 
     // invalid type, cannot contain o 
    } 
    return false; 
} 

雖然這會工作,但看起來很亂(我不喜歡以這種方式使用例外)。

有沒有更好的方法來實現這個相同的目標(改變方法簽名是不允許的)?

編輯:是的,這不起作用由於E被擦除對象:(

+3

它工作嗎?你會得到一個未經檢查的鑄造編譯器警告。你永遠不會得到一個ClassCastException(除非'o'不是'E'的**擦除**的實例,可能是'Object')。 –

+0

您能否詳細說明「要求我有一個與類的泛型E兼容的類型?」謝謝。 –

+0

我同意 - 這是行不通的。你確實需要實際的類做適當的演員,可能會失敗。 – StaxMan

回答

4

這是唯一可行的:(1)通過在預期Class,或(2)檢查的一些泛型類型參數。定義<E>反射元件讓我解釋

最常見的情況是隻需要調用程序在運行時類的E通過

public class MyClass<E> { 
    private final Class<E> realType; 
    public MyClass(Class<E> realType) { 
     this.realType = realType; 
    } 
    public boolean Contains(Object o) { 
     E e = realType.cast(o); // runtime cast - will throw ClassCastException. 
     // Could also use realType.isInstance(o) 
     // or realType.isAssignableFrom(o.getClass()) 
     ... 
    } 
} 

來電:。

new MyClass<MyObject>(MyObject.class) 

這通常是類型安全的,因爲編譯器將驗證<E>的比賽。當然,調用者可以繞過編譯器的檢查...你無能爲力!對於(2),我的意思是你可以使用反射來檢查靜態泛型類型參數。在你的情況下,這可能不是一個好的選擇,因爲你必須有權訪問某些靜態定義爲<E>的字段,方法或超類聲明。最常見的方法是讓你的課程抽象並讓調用者擴展它。 Hamcrest的TypeSafeMatcher(見ReflectiveTypeFinder)使用此方法,效果很好。 (請注意,TypeSafeMatcher基本上只是讓選項(1)更容易爲程序員;它仍然提供了一個構造函數,該類用於反射不起作用的情況!)如果你想變得很花哨,你可以檢查getClass().getGenericSuperclass().getActualTypeArguments()。這並不像聽起來那麼容易 - 見this good article。我甚至不確定那篇文章是否涵蓋了所有的邊界情況 - 你基本上是在重新編譯編譯器!所以只需要選擇(1)並且很高興你在C#中不使用泛型:-)

+0

如果你像這樣手動設置類型,泛型的要點是什麼? –

+0

泛型仍然通過消除大多數強制轉換和改進編譯器類型檢查來清理源代碼。不是捍衛Java使用類型擦除的決定,但.NET泛型也有缺點。 –

0

我不明白你爲什麼這樣用.contains()搞亂。 Collection中的規範說,如果給定對象等於(在.equals()的意義上)容器的某個元素,它將返回true。那麼你是否在改變.contains()的含義?你的平等是如何定義的?

+0

Java TreeSet類不使用equals()方法來定義相等性(而是使用compareTo()方法或比較器)。試圖將不能轉換的對象傳遞給TreeSet的泛型參數是一個ClassCastException(它是contains()方法的Javadoc的一部分)。我有類似的情況與不同類型的樹狀數據結構。有趣的是,Javadoc確實說它對contains()使用equals()方法。 – helloworld922

+0

@ helloworld922:「嘗試將不能轉換的對象傳遞給該TreeSet的泛型參數是一個ClassCastException」這不是事實。運行時不存在通用參數。所有TreeSet在運行時都會將對象轉換爲Comparable,然後使用「compareTo」將其與其他對象進行比較。只有當您輸入的對象不能相互比較時纔會出現ClassCastException。 – newacct

+0

啊,真的。我習慣於讓Comparable對象通常與自己相媲美,但是你說得對,那並不需要是真實的。 – helloworld922