2012-12-25 28 views
2

我有一個接口對象轉換爲字符串:具有無界通配符的Java泛型?

public interface Converter<T> { 
    String asString(T object); 
} 

和地圖來存儲所有可用的轉換器:

Map<Class<?>, Converter<?>> converterMap; 

現在我有異構數據的列表,以這樣的轉換:

List<?> data = fetchData(); 
List<String> stringData = new ArrayList<>(data.size()); 
for (Object datum : data) { 
    stringData.add(convertrMap.get(datum.getClass()).asString(datum)); 
} 

但是這段代碼沒有編譯:

error: method asString in interface Converter<T> cannot be applied to given types; 
      stringData.add(converterMap.get(datum.getClass()).asString(datum)); 
    required: CAP#1 
    found: Object 
    reason: actual argument Object cannot be converted to CAP#1 by method invocation conversion 
    where T is a type-variable: 
    T extends Object declared in interface Converter 
    where CAP#1 is a fresh type-variable: 
    CAP#1 extends Object from capture of ? 

我該如何更改代碼?

+0

你爲什麼不使用'asString(Object對象)',而不是使用'轉換器'然後使用'Converter '它使用泛型冗餘。 – Shivam

+0

這可行,但類型信息丟失,使得'asString'方法不安全,例如我可以安全地使用'Converter dateConverter =(Converter )converterMap.get(Date.class)''。 –

+1

我看不出它是如何變得不安全的。即使您目前的設計是不安全的,如果您錯誤地映射轉換器對象和類。聲明如'Converter c = new Converter ; c.asString(「abc」);'是非法的,因爲'?'在運行時是未知的,因此它只會期望這個未知類型的一些子類型。 – Shivam

回答

4

您所面臨的問題稱爲wildcard capture。 Java無法識別將從List<?>數據收到的類型。 嘗試在任何兩種方式重構你的代碼

方法1:更改界面如下

interface Converter { 
    String asString(Object object); 
} 

方法2:輔助方法,通過類型推斷捕捉野生卡

創建如下輔助方法,

// Helper method created so that the wildcard can be captured 
// through type inference. 
private <T> void helper(List<T> data) { 
    Map<Class<?>, Converter<T>> converterMap = null; 
    List<String> stringData = null; 

    for (T datum : data) { 
     stringData.add(converterMap.get(datum.getClass()).asString(datum)); 
    } 
} 

調用此helper方法如下

List<?> data = fetchData(); 
helper(data); 
+0

我認爲將參數類型改爲「對象」是不安全的,例如,我可以在整數轉換器上調用'asString(「」)''。我也有一些其他代碼需要這種類型的信息。 –

+0

檢查更新的答覆。 – Jayamohan

+1

方法2的工作原理!雖然它看起來有點醜...謝謝! –

0

你應該改變這樣的代碼:

public interface Converter { 
    String asString(Object object); 
} 

我認爲它會工作。

1

首先,你應該封裝映射中的輔助類像這樣,其操作保持不變(即Class<T>映射到Converter<T>)內:

public class ConverterMap { 
    Map<Class<?>, Converter<?>> converterMap = new HashMap<Class<?>, Converter<?>>(); 
    public <T> void addConverter(Class<T> clazz, Converter<T> converter) { 
     converterMap.put(clazz, converter); 
    } 
    @SuppressWarnings("unchecked") 
    public <T> Converter<T> getConverter(Class<T> clazz) { 
     return (Converter<T>)converterMap.get(clazz); 
    } 
} 

現在,分解任務,讓我們的小

ConverterMap cm = new ConverterMap; 
private static String convert(Object x); 

這看似簡單,但難度比它看起來,因爲你會:寫一個函數,它接受任何對象,並將其轉換基於轉換地圖上(假設該對象的類是在轉換地圖)的步跑成爲Java類型系統的一個特例,其類型爲.getClass()。你將會有問題說服編譯器xx.getClass()參數的一個實例。要解決這個問題的最好辦法是:

@SuppressWarnings("unchecked") 
private static <T> String convert2(Class<T> clazz, Object x) { 
    return cm.getConverter(clazz).asString((T)x); 
    // you can alternately do clazz.cast(x) instead of the unchecked cast (T)x 
} 
private static String convert(Object x) { 
    return convert2(x.getClass(), x); 
} 

然後你就可以解決問題的其餘部分:

for (Object datum : data) { 
    stringData.add(convert(datum)); 
}