我有一个将对象转换为字符串的接口:
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 :(得分: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类型系统的特殊情况。您将有一个问题,即说服编译器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));
}
答案 2 :(得分:0)
你应该改变这样的代码:
public interface Converter {
String asString(Object object);
}
我认为它会奏效。