[英]Comparision between two different specialization of the Generic class in Java
我正在實現自定義對象的優先級隊列。 自定義對象類如下所示,是否建議將其實現為泛型類,其中T只是與messageId關聯的有效負載?
public class PriQMsgPayload <T> implements Comparable<PriQMsgPayload<T>>{
private Integer mMsgNum;
private T ExtraDataForMsg = null;
public PriQMsgPayload(T extra, PluginConfig.PriQMessage msg)
{
mMsgNum= msg;
ExtraDataForMsg = extra;
}
@Override
public int compareTo(PriQMsgPayload<T> another) {
return mMsgNum.compareTo(another.mMsgNum);
}
}
我懷疑:在下面提到的情況下,我首先插入專門用於Integer的P1,當我插入另一個專門用於MyClass的對象P2時,比較器會不會混淆?
PriQMsgPayload<Integer> P1 = new PriQMsgPayload<Integer>(2, 1);
PriQMsgPayload<MyClass> P2 = new PriQMsgPayload<MyClass>(new MyClass(), 2);
我認為沒有任何意義。 當你試圖將對象放入隊列時會出現問題 - 隊列沒有完全煮熟的類型,它允許它包含這兩個類型!
考慮:
PriorityQueue queue; // works, but is raw
PriorityQueue<PriQMsgPayload> queue; // works, but is partially raw
PriorityQueue<PriQMsgPayload<Object>> queue; // won't accept either object
你可以將它們放入這種類型的隊列中(我最初說你不能,但是@newacct很好地糾正了我) :
PriorityQueue<PriQMsgPayload<?>> queue; // won't accept either object
但這涉及丟棄有關您擁有哪種額外數據的類型信息。
任何允許您添加兩個對象的隊列類型都不會保留額外數據的類型; 這意味着當你再次拿出對象時,你會丟失所有類型的信息。 沒有辦法編寫安全地將對象作為PriQMsgPayload<Integer>
而不是PriQMsgPayload<MyClass>
提取的代碼。
好吧,理想情況下,您應該無法將PriQMsgPayload<Integer>
與PriQMsgPayload<String>
- 因為PriQMsgPayload<Integer>
實現了Comparable<PriQMsgPayload<Integer>>
,因此其compareTo
方法采用PriQMsgPayload<Integer>
,所以它不期望PriQMsgPayload<String>
。
在編譯時和運行時嘗試執行此操作時會發生什么,取決於您的優先級隊列類的聲明方式及其工作方式。 您沒有告訴我們您正在使用的優先級隊列的實現。
假設您有一個通用優先級隊列類,聲明如下:
class MyPriorityQueue<E extends Comparable<? super E>> { ... }
即它要求其類型參數與其自身相當。 那么就不可能聲明一個優先級隊列,你可以將PriQMsgPayload<Integer>
和PriQMsgPayload<String>
插入到; 因為例如MyPriorityQueue<PriQMsgPayload<?>>
將不被允許,因為PriQMsgPayload<?>
不滿足邊界。
如果您使用的是java.util.PriorityQueue
,則問題是PriorityQueue
類(以及TreeSet
和TreeMap
等)不是類型安全的。 您可以創建一個new PriorityQueue<Object>()
,然后將任何類型的對象插入其中(完全不可比較的對象),並且它不會在編譯時捕獲任何內容,僅在運行時失敗(或者它可能正常工作) ,取決於對象的類型)。
在這種情況下運行時會發生什么? 好吧,在類型擦除之后,在運行時,類等同於:
public class PriQMsgPayload implements Comparable {
// ....
public int compareTo(Object another) {
return compareTo((PriQMsgPayload)another);
}
public int compareTo(PriQMsgPayload another) {
return mMsgNum.compareTo(another.mMsgNum);
}
}
它只是比較整數,因此在這種情況下,在運行時,它將正常工作。
為什么PriorityQueue
不安全? 好吧,如果你看一下這個類,它的設計可以用兩種方式之一:你可以提供一個自定義Comparator
,或者它將使用對象的“自然順序”。 如果是后者,你應該提供與自己相當的東西,但這不會在編譯時以任何方式強制執行。 因為您可以將它與自定義比較器(方式#1)一起使用,它適用於任何類型,它們不能在類的類型參數上放置任何邊界,就像我們上面的MyPriorityQueue
。
實際上,有一種方法可以使PriorityQueue
類型安全,並且仍然能夠支持自定義比較器和自然排序模式。 只需刪除使用自然排序的構造函數(保留使用比較器的構造函數,因為它們是類型安全的),而是用工廠方法替換它們。 例如,構造函數
PriorityQueue(int initialCapacity)
可以用工廠方法代替
public static <T extends Comparable<? super T>> PriorityQueue<T> construstUsingNaturalOrdering(int initialCapacity) {
return new PriorityQueue<T>(initialCapacity, new NaturalOrderingComparator<T>());
}
這樣,工廠方法綁定的泛型確保自然排序優先級隊列只能為與自身相當的類型構建。 我不知道為什么Java庫設計者沒有這樣做。
你到底什么意思?
你不能在P1
上調用compareTo()
傳遞P2
作為參數(反之亦然); 這會給你一個編譯錯誤,因為P1.compareTo()
需要一個PriQMsgPayload<Integer>
,所以你不能傳遞PriQMsgPayload<MyClass>
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.