2016-03-03 128 views
7

爲什麼不允許定義這樣的靜態成員:泛型參數聲明靜態成員

private static final <T extends Object> Map<Class<T>, BiFunction<T, T, Boolean>> SPECIFIC_HANDLERS = new HashMap<>(); 

相反,它只是允許使用非指定:

private static final Map<Class<?>, BiFunction<?, ?, Boolean>> SPECIFIC_HANDLERS = new HashMap<>(); 

有一種解決方法,使我可以定義BiFunction的兩個參數必須是相同的類型,並且Map的鍵必須是這些參數的類類型?

更新澄清(因爲@Mena的建議是不適合我):

我要地圖爲陣的equals方法的通用等於幫手。通用助手正式接收兩個對象。如果它們是數組,我必須將它傳遞給重載的Arrays.equals()方法之一。我wandted具有用於正確的方法的查找(實施例1):

private static final Map<Class<?>, BiFunction<?, ?, Boolean>> ARRAY_EQUALS_HANDLER = new HashMap<>(); 

static 
{ 
    ARRAY_EQUALS_HANDLER.put(Object[].class, (l, r) -> Arrays.equals((Object[]) l, (Object[]) r)); 
    ARRAY_EQUALS_HANDLER.put(boolean[].class, (l, r) -> Arrays.equals((boolean[]) l, (boolean[]) r)); 
    .... 
} 

,然後用它喜歡:

boolean equal = ARRAY_EQUALS_HANDLER.get(anObj1.getClass()).apply(anObj1, anObj2); 

的構建體(如Mena的)甚至不編譯:

private static <T extends Object> Map<Class<T>, BiFunction<T, T, Boolean>> getSpecificHandlers() 
{ 
    Map<Class<T>, BiFunction<T, T, Boolean>> result = new HashMap<>(); 
    result.put(Object[].class, (l, r) -> Arrays.equals((Object[]) l, (Object[]) r)); 
    result.put(boolean[].class, (l, r) -> Arrays.equals((boolean[]) l, (boolean[]) r)); 
    return result; 
} 

如果我填充方法外部記錄所產生的地圖:

@SuppressWarnings({ "unchecked", "rawtypes" }) 
private static final Map<Class<?>, BiFunction<?, ?, Boolean>> ARRAY_EQUALS_HANDLER = (Map) getSpecificHandlers(); 

static 
{ 
    ARRAY_EQUALS_HANDLER.put(Object[].class, (l, r) -> Arrays.equals((Object[]) l, (Object[]) r)); 
    ... 
} 

然後整個類型安全已經消失了,因爲我必須在將它分配給最終靜態成員時執行(未檢查)類型轉換。

我的例子1以上的作品,但我一定要投收到的拉姆達使用時:

private static <T extends Object> boolean equalsArray(T anArray, T anOtherArray) { 
    Object o = ARRAY_EQUALS_HANDLER.get(anArray.getClass()); 
    @SuppressWarnings("unchecked") 
    BiFunction<T, T, Boolean> func = (BiFunction<T, T, Boolean>) o; 

    Boolean result = func.apply(anArray, anOtherArray); 
    return result; 
} 
+2

,則只能有一個實例。泛型在這種情況下是沒有意義的......或者,也許它是單身? –

+0

如果它是靜態的而不是最終的,那麼可能會有很多分配給該引用。 –

+1

@ReutSharabani其實,它是'靜態'_and_''最後'... –

回答

3

您使用的是通用的方法成語(返回類型前的類型參數的聲明),爲一個常數宣言。

該成語不會編譯。

正如其他地方所提到的,您不能在靜態上下文中使用類泛型類型。

可以做些什麼作爲一種解決方法是聲明一個靜態方法,而不是一個恆定的 - 在東西線:

private static final <T extends WhateverBound> Map<Class<T>, 
    BiFunction<T, T, Boolean>> 
    getSpecificHandlers(T t) { 

    // return new HashMap<Class<T>, BiFunction<T, T, Boolean>>(); 
    // much easier on the eye - thanks Andy Turner 
    return new HashMap<>(); 
} 

假設:

static class WhateverBound{} 
static class Foo extends WhateverBound {} 

然後,您可以調用你的方法爲:

Map<Class<Foo>, BiFunction<Foo, Foo, Boolean>> map = 
getSpecificHandlers(new Foo()); 

當然注意這裏的關鍵字final具有非常不同的含義,可能完全可以省略。

或...

您可以將其保持爲常量,並在所有類型參數化中重複通配符/邊界模式。

例如:

private static final Map<Class<? extends WhateverBound>, 
    BiFunction<? extends WhateverBound, ? extends WhateverBound, Boolean>> 
     SPECIFIC_HANDLERS = new HashMap<>(); 

static { 
    SPECIFIC_HANDLERS.put(Foo.class, (f, ff) -> true); 
} 
+2

鑽石操作員不在這裏工作嗎? –

+0

@AndyTurner它**會**!感謝您的注意。 – Mena

+0

感謝您的想法。但爲了我的目的,這是行不通的。構造 – Heri

0

我有一個解決方案,這是一個有點難看,但要求的作品。

第一件事情,請注意Map.get()方法獲得原始Object類型作爲參數,因此它是不可能的編譯器猜測基礎上的參數類型此方法的返回類型。而不是返回類型來自該領域本身的聲明,並且因爲它是使用固定類型參數聲明的(在您的情況下它應該是Object) - 對於任何調用.get(),您都會得到固定返回類型。實際上,同樣的事情不僅適用於get()方法,而且適用於所有您期望用作類型參數化方法的方法。其次,期望的結構應該對鍵和值對(您希望值類型取決於鍵類型)添加一些限制。這通常通過類型參數完成。它不適用於字段聲明,但適用於類型或方法聲明。因此,在這兩個優點下,我以解決方案結束:引入類型,擴展Map與附加類型的關聯,並附加通用的get()方法(當然,您可以添加更多的通用方法)。

public static class HandlerMap<T> extends HashMap<Class<? extends T>, BiFunction<T, T, Boolean>> { 
    @SuppressWarnings("unchecked") 
    <U extends T> BiFunction<U, U, Boolean> getStrict(Class<? extends U> key) { 
     return (BiFunction<U, U, Boolean>) get(key); 
    } 
} 

private static HandlerMap<Object> ARRAY_EQUALS_HANDLER = new HandlerMap<>(); 

static { 
    ARRAY_EQUALS_HANDLER.put(Object[].class, (l, r) -> Arrays.equals((Object[]) l, (Object[]) r)); 
    ARRAY_EQUALS_HANDLER.put(boolean[].class, (l, r) -> Arrays.equals((boolean[]) l, (boolean[]) r)); 

    //WARNING - type safety breaks here 
    ARRAY_EQUALS_HANDLER.put(int[].class, (l, r) -> Arrays.equals((boolean[]) l, (boolean[]) r)); 

} 
public static void main(String[] args) throws Exception { 
    BiFunction<int[], int[], Boolean> biFunction = ARRAY_EQUALS_HANDLER.getStrict(int[].class); 
} 

我用HashMap作爲超類直接用於較短的代碼。請注意,getStrict()方法被標記爲不安全(顯然,它是)。並且注意put()上也缺少類型coersion。

我想現在,所有的醜陋都來自於你實際上並不想要的Map,但有些不同。是的,有一個共同的概念 - 有鍵值對的結構,但類型轉換應該以另一種方式工作。 Map用於限制某些類型和值的鍵到另一種類型,但不需要它。你需要的是更強大的東西 - 不僅爲鍵和值分別鍵入coersion,而且還在鍵類型和值類型之間進行綁定。

0

我不認爲你只需要使用成員聲明就可以了。你可以做的是隱藏地圖本身,並提供更嚴格的方法訪問的地圖。

例如,像這樣:

class FunctionMapper { 

    private final Map<Class<?>, BiFunction<?, ?, Boolean>> MAP = new HashMap<>(); 

    public <T> void map(Class<T> c, BiFunction<T, T, Boolean> f) { 
     MAP.put(c, f); 
    } 

    public <T> BiFunction<T, T, Boolean> get(Class<T> c) { 
     return (BiFunction<T, T, Boolean>) MAP.get(c); 
    } 

    public <T> Boolean apply(T o1, T o2) { 
     return get((Class<T>) o1.getClass()).apply(o1, o2); 
    } 
} 

在你平等的幫手,你可以使用它是這樣的:如果它是一個靜態成員

FunctionMapper mapper = new FunctionMapper(); // make this static final 
    mapper.map(String.class, (s1, s2) -> s1.compareTo(s2) == 0); 
    System.out.println(mapper.apply("foo", "bar"));