java - 是否需要反射才能动态地将正确的通用适配器应用于我的对象

标签 java generics reflection polymorphism xmladapter

我目前正在开发一个使用通用类型适配器库的序列化例程。如果正在序列化的对象是我拥有的特定适配器之一的实例,那么我需要在执行其他序列化过程之前在该对象上调用该适配器。

以下代码有效:

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)参数,未在签名中的其他位置使用;
  • clazzT 类型的父类(super class)型,因为我们检查了 clazz.isInstance(obj);
  • 适配器可以处理clazz的实例或其父类(super class)型,因为 map 就是这样构建的;
  • 因此,adapter 可以处理(未知)父类(super class)型 T 的所有实例,因此 ? super T 声明。

关于java - 是否需要反射才能动态地将正确的通用适配器应用于我的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29780226/

相关文章:

C# foreach 在一个接口(interface)的集合上

java - Java 11 中的高效空检查

java - 纠正和改进java代码

java - 使用 Jackson 反序列化 Guava 的表抛出 "JsonMappingException: No suitable constructor found for type"

android - 外投影类型禁止使用方法

Scala - 如何使用反射 api 获取字段的 'type'

java - 从时间戳构造java.util.Date对象时发生时区问题

function - Kotlin:覆盖(并指定)通用接口(interface)函数

ios - swift 。通过字符串名称引用实例变量

c++ - 如何向 C++ 应用程序添加反射?