2

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

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 ?

我应该如何更改代码?

4

3 回答 3

4

您面临称为通配符捕获的问题。Java 无法识别将从List<?>数据中接收的类型。尝试以两种方式中的任何一种重构您的代码

方法1:改变你的界面如下

interface Converter {
    String asString(Object object);
}

方法二:通过类型推断捕获通配符的辅助方法

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

// 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);
于 2012-12-25T03:02:04.947 回答
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()。您将遇到说服编译器x是参数实例的问题x.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));
}
于 2012-12-25T09:28:14.763 回答
0

您应该像这样更改代码:

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

我认为它会起作用。

于 2012-12-25T03:03:11.113 回答