我目前正在开发一个使用通用类型适配器库的序列化例程。如果正在序列化的对象是我拥有的特定适配器之一的实例,那么我需要在执行其他序列化过程之前在该对象上调用该适配器。
以下代码有效:
private final static String serialize(Object obj, Map<Class<?>,
XmlAdapter<?,?>> classToAdapterMap) throws JAXBException
{
Object adaptedObj = null;
for (Class<?> clazz : classToAdapterMap.keySet()) {
if (clazz.isInstance(obj)) {
XmlAdapter<?,?> adapter = classToAdapterMap.get(clazz);
Class<?>[] argTypes = new Class[] {clazz};
try {
Method method = adapter.getClass().getMethod("marshal", argTypes);
adaptedObj = method.invoke(adapter, obj);
break;
} catch (Exception e) {
// handle method retrieval and invocation related exceptions
}
}
}
// serialize
}
但是,我最初认为我可以更简单地做到这一点,例如使用如下代码:
/* DOES NOT WORK */
private final static String serialize(Object obj, Map<Class<?>,
XmlAdapter<?,?>> classToAdapterMap) throws JAXBException
{
Object adaptedObj = null;
for (Class<?> clazz : classToAdapterMap.keySet()) {
if (clazz.isInstance(obj)) {
XmlAdapter<?,?> adapter = classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(clazz.cast(obj));
break;
}
}
// serialize
}
显然,问题在于通配符通用类型适配器不能保证处理 clazz
类型的对象。 。但是,我无法通过将方法签名更改为 private final static <T> String serialize(Object obj, Map<Class<T>, XmlAdapter<?,T>> classToAdapterMap)
来表明这两者是相同的(否则我可能会这样做) ,因为 map 需要容纳所有不同类型的适配器。
什么是更好的方法来做到这一点?或者我应该坚持使用基于反射的解决方案?
提前致谢,
-丹
最佳答案
有多种解决方案可以解决此问题。
很可能,最简单的一种是使用原始类型:不要为适配器
指定类型参数,编译器将高兴 接受 marshall
调用(当然带有原始类型警告):
XmlAdapter adapter = classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(obj);
(这实际上与 Bastian 的解决方案大致相同,没有中间类型)
如果您不喜欢原始类型,您可以选择未经检查的强制转换到对象
参数化适配器。它并不是真的更好,但它也可以工作(通过欺骗编译器......):
XmlAdapter<?, Object> adapter = (XmlAdapter<?, Object>) classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(obj);
我的最后一个解决方案是在方法级别使用类型参数。这次,您所做的在语义上是正确的(只要 map 本身是正确的),并且未经检查的强制转换实际上意味着“我知道我在这里做什么”:
private final static <T> String serialize(T obj, Map<Class<?>,
XmlAdapter<?,?>> classToAdapterMap) throws JAXBException
{
Object adaptedObj = null;
for (Class<?> clazz : classToAdapterMap.keySet()) {
if (clazz.isInstance(obj)) {
try {
XmlAdapter<?, ? super T> adapter = (XmlAdapter<?, ? super T>) classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(obj);
break;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// serialize
}
语义正确性来自于以下内容:
- 您可以将
T
视为obj
的实际类,因为T
是方法绑定(bind)参数,未在签名中的其他位置使用; clazz
是T
类型的父类(super class)型,因为我们检查了clazz.isInstance(obj)
;适配器
可以处理clazz
的实例或其父类(super class)型,因为 map 就是这样构建的;- 因此,
adapter
可以处理(未知)父类(super class)型T
的所有实例,因此? super T
声明。
关于java - 是否需要反射才能动态地将正确的通用适配器应用于我的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29780226/