2014-02-23 37 views
1

有沒有辦法創建一個實現比較器的類,並且只允許創建一個單例實例,或者每個參數化類型只失敗一個實例?Java通用比較器單例

我嘗試了一些機制(都使用私有構造函數,然後getInstance方法),但都沒有有效的語法。所以我想知道是否類型擦除意味着不可能依賴泛型Comparator類的單個實例。

其目的是爲了避免有幾十個類的實例,因爲所有的行爲都是相同的,所以唯一的例子似乎是可取的。可能嗎?如果沒有,建議如何避免比較器實例的大量複製?

編輯:這個問題似乎引起了我想要實現的困惑,所以我會在這裏粘貼一個我的破解企圖。下面的代碼試圖創建一個僅包含每個類型T的比較器的單個實例的Map。這是有效的語法,但我無法調用getInstance方法,因爲我無法獲取Class<T>實例客戶端代碼。

final class IntervalComparator<T extends Comparable<T>> implements 
     Comparator<Interval<T>> { 

    // Private constructor. Use the getInstance method instead. 
    private IntervalComparator() { 
    } 

    // Map to hold one instance for each type. 
    private static Map<Class, IntervalComparator> cacheMap = new HashMap<>(); 

    // The only method which returns an instance of this Comparator. 
    public static <K extends Comparable<K>> IntervalComparator<K> 
      getInstance(Class<K> type) { 
     IntervalComparator<K> soleInstance = cacheMap.get(type); 
     if(soleInstance == null) { 
      soleInstance = new IntervalComparator<>(); 
      cacheMap.put(type, soleInstance); 
     } 
     return soleInstance; 
    } 

    @Override 
    public int compare(Interval<T> o1, Interval<T> o2) { 
     // ... Comparison code goes here ... 
    } 
} 

客戶端代碼如下所示:

private Set<Interval<T>> intervalSet; 
private IntervalUnion(IntervalUnion.Builder<T> builder) { 
    this.intervalSet = new TreeSet<>(new IntervalComparator<T>()); 
    // ... add Interval<T> objects into intervalSet ... 
} 

目前它正在創建每次調用時間new IntervalComparator<T>對象,我不認爲有任何的方式來傳遞Class<T>getInstance方法。

+1

向我們展示你的代碼。 – SLaks

+0

你如何調用'getInstance()'方法?爲什麼你不能傳遞'Class'實例? –

+0

由於客戶端代碼也是參數化的,所以我沒有具體的類型傳遞給getInstance方法。 – Bobulous

回答

1

我相信你可以爲你的目的使用一個比較對象。

我假設你的比較器實現IntervalComparator是無狀態的(就其沒有字段來存儲任何東西)而言。在這種情況下,您始終可以使用相同的代碼進行比較。然後

final class IntervalComparator<T extends Comparable<T>> implements 
    Comparator<Interval<T>> { 

    // Private constructor. Use the getInstance method instead. 
    private IntervalComparator() { 
    } 

    private static IntervalComparator INSTANCE = new IntervalComparator(); 

    @SuppressWarnings("unchecked") 
    public static <K extends Comparable<K>> IntervalComparator<K> getInstance() { 
     return (IntervalComparator<K>)INSTANCE; 
    } 

    @Override 
    public int compare(Interval<T> o1, Interval<T> o2) { 
     // ... Comparison code goes here ... 
    } 
} 

你的使用情況將如下:

private Set<Interval<T>> intervalSet; 

private IntervalUnion() { 
this.intervalSet = 
    new TreeSet<Interval<T>>(
     IntervalComparator.<T>getInstance()); 
// ... add Interval<T> objects into intervalSet ... 
} 

注意,有在多態getInstance()方法的註釋@SuppressWarnings("unchecked")。這是爲了抑制編譯器警告,由於Java泛型中的type erasure,無法檢查該方法中發生的強制轉換。

順便說一下,這樣的實施有一個突出的說服性的先例。該方法java.util.Collections.emptyList()同樣實現:

@SuppressWarnings("unchecked") 
public static final List EMPTY_LIST = new EmptyList<>(); 
// ... 

@SuppressWarnings("unchecked") 
public static final <T> List<T> emptyList() { 
    return (List<T>) EMPTY_LIST; 
} 

(參見http://www.docjar.com/html/api/java/util/Collections.java.html#3161

+0

完成了這項工作(所有640個單元測試都通過了,所以沒有任何事情因爲你所建議的更改而中斷)。我確實認爲它可能歸結爲單個實例和演員陣容,但我無法弄清楚如何將參數化類型傳遞給靜態方法調用。我見過'ClassName。 methodName();'語法之前,但我不記得在哪裏。你有描述這種語法的鏈接嗎? – Bobulous

+1

這個構造的語法和語義在這裏被定義:http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12(然而,非常強壯的肉)。 – Matt