带有无界通配符的Java泛型?

时间:2012-12-25 02:43:42

标签: java generics map wildcard

我有一个将对象转换为字符串的接口:

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 ?

我该如何更改代码?

3 个答案:

答案 0 :(得分:4)

您正面临名为wildcard capture的问题。 Java无法识别将从List<?>数据接收的类型。 尝试以两种方式重构代码

方法1:按以下方式更改界面

interface Converter {
    String asString(Object object);
}

方法2:通过类型推断捕获通配符的Helper方法

创建一个帮助方法,如下所示,

// 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));
    }
}

调用此辅助方法如下

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

答案 1 :(得分: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);

这看起来很简单,但比它看起来更难,因为你会遇到类型为.getClass()的Java类型系统的特殊情况。您将有一个问题,即说服编译器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));
}

答案 2 :(得分:0)

你应该改变这样的代码:

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

我认为它会奏效。