【问题标题】:Unchecked cast from Object to List从对象到列表的未经检查的转换
【发布时间】:2016-03-15 20:55:08
【问题描述】:

我正在尝试扩展here 发布的问题的解决方案。

那里共享的 castList 方法非常适用于非泛型类型。

public static <T> List<T> castList(Object obj, Class<T> clazz)
{
    List<T> result = new ArrayList<T>();
    if(obj instanceof List<?>)
    {
        for (Object o : (List<?>) obj)
        {
            result.add(clazz.cast(o));
        }
        return result;
    }
    return null;
}

我们遇到的问题是,当返回类型不是像字符串列表这样简单的东西,而是当列表本身是另一个泛型的时候,比如地图列表时,我们尝试使用相同的想法。

List<Map<String, Object>> list = (List<Map<String, Object>>) out.get("o_cursor");

尝试按如下方式使用 castList 会产生类型不匹配:无法从 List 转换为 List>。有没有办法扩展 castList 来做到这一点?

List<Map<String, Object>> list = castList(out.get("o_cursor"), Map.class);

我尝试了特定于地图列表的修改版本,但仍然收到错误“方法 castMapList(Object, Class>) 不适用于参数 (Object, Class)" 这似乎是同一个错误的不同风格。

public static <K, V> List<Map<K, V>> castMapList(Object object, Class<Map<K, V>> clazz) {        
    if (object instanceof List<?>) {
        List <Map<K, V>> result = new ArrayList<Map<K, V>>();

        for (Object o: (List<?>) object) {
            result.add(clazz.cast(o));
        }

        return result;
    }

    return null;
}

【问题讨论】:

    标签: java generics


    【解决方案1】:

    由于擦除,有:

    1. 没有Class&lt;Map&lt;K, V&gt;&gt;
    2. 没有内置方法可以在运行时检查每个特定的 Map 是否具有特定的 KV

    即:

    1. 如您所见,每个泛型类都有一个对应的原始类型 Class 对象,例如Map.classClass&lt;Map&gt;
    2. obj instanceof Map&lt;K, V&gt; 之类的东西是编译器错误,Class 对象不检查通用参数,例如isInstancecast

    重点是,如果没有一些额外的工作,您将无法在运行时验证泛型。

    您仍然可以围绕它进行编程,具体取决于您在做什么。例如,一种简单的方法就是重建每个Map,就像重建List一样:

    static <K, V> List<Map<K, V>> castMapList(
            Object obj, Class<K> keyType, Class<V> valueType) {
        if (obj instanceof List<?>) {
            List<Map<K, V>> result = new ArrayList<>();
    
            for (Object element : (List<?>) obj) {
                if (element instanceof Map<?, ?>) {
                    Map<K, V> map = new HashMap<>();
    
                    for (Map.Entry<?, ?> entry : (Map<?, ?>) element) {
                        map.put(keyType.cast(entry.getKey()),
                                valueType.cast(entry.getValue());
                    }
    
                    result.add(map);
                } else {
                    // do something that you want?
                    // personally I would throw ClassCastExceptions
                    // but you seem to be returning null
                }
            }
    
            return result;
        }
        return null;
    }
    

    此外,例如,您似乎在进行某种反序列化,您可以将Class 直接烘焙到被序列化的Map

    public class MyCheckedMap<K, V>
    extends SomeMapImplementation<K, V>
    implements Serializable {
        private static final long serialVersionUID = 1L;
        private final Class<K> keyType;
        private final Class<V> valueType;
    
        public MyCheckedMap(Class<K> keyType, Class<V> valueType) {
            this.keyType = Objects.requireNonNull(keyType);
            this.valueType = Objects.requireNonNull(valueType);
        }
    
        @SuppressWarnings("unchecked")
        public static <K, V> MyCheckedMap<K, V> cast(
                Object obj, Class<K> keyType, Class<V> valueType) {
            if (!(obj instanceof MyCheckedMap<?, ?>))
                throw new ClassCastException(obj.getClass().toString());
    
            MyCheckedMap<?, ?> map = (MyCheckedMap<?, ?>) obj;
            validate(keyType, map.keyType);
            validate(valueType, map.valueType);
    
            return (MyCheckedMap<K, V>) obj;
        }
    
        private static void validate(Class<?> lhs, Class<?> rhs) {
            if (lhs == rhs)
                return;
            throw new ClassCastException(String.format("%s != %s", lhs, rhs));
        }
    }
    

    您必须为每个 ListMap 等编写一些样板代码,但可能不会比您看起来做的多。

    还有一些其他方法可以解决这类问题,例如 Neal Gafter 的 Super Type Tokens 和 Guava 的 TypeToken,这是 Neal Gafter 想法的衍生。这些本质上是使用反射来实现类似MyCheckedMap

    【讨论】:

    • 感谢您提供的信息。这非常有帮助。
    【解决方案2】:

    我不确定是否有未经检查的警告对您来说是个问题。对于地图,您可以执行以下操作:

    public <K, V> List<Map<K, V>> castMapList(Object object, Class<K> class1, Class<V> class2) {
        if (object instanceof List<?>) {
            List<Map<K, V>> result = new ArrayList<>();
    
            for (Map<K, V> o : (List<Map<K, V>>) object) { //Unchecked cast
                result.add(o);
            }
    
            return result;
        }
    
        return null;
    }
    

    for (Object o : (List<?>) object) {
        if (o instanceof Map<?, ?>) {
            result.add((Map<K, V>) o); //Unchecked cast
        }
    }
    

    我不认为有抑制警告对你来说是最坏的情况,因为即使在来自Class.javacast method 中,他们也会这样做。不过,您必须确保该列表包含地图。

    【讨论】:

    • “我不认为有一个抑制警告对你来说是最坏的情况,因为即使在 Class.java 的 cast 方法中,他们也会这样做。” 那是'不好推理,因为cast 使用isInstance 来证明未经检查的演员表是正确的。 isInstance 无法验证通用参数(即它只能检查Map,而不是&lt;K, V&gt;,这就是为什么只有Class&lt;Map&gt;)所以你将苹果与橙子进行比较。如果我们要进行全面的未经检查的强制转换,那么我们不妨将 OP 的 out.get 的整个结果像 (List&lt;Map&lt;String, Object&gt;&gt;) out.get("o_cursor") 一样强制转换。
    【解决方案3】:

    这就是我想出的。再次感谢!

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class CastUtility {
    
        public static <T> List<T> castList(Object object, Class<T> clazz) {
            if (object == null) {
                return null;
            }
    
            if (object instanceof List<?>) {
                List<T> result = new ArrayList<T>();
    
                for (Object o : (List<?>) object) {
                    result.add(clazz.cast(o));
                }
    
                return result;
            }
    
            throw new ClassCastException();
        }
    
        public static <K, V> Map<K, V> castMap(Object object, Class<K> keyType,
                Class<V> valueType) {
    
            if (object == null) {
                return null;
            }
    
            if (object instanceof Map<?, ?>) {
                Map<K, V> result = new HashMap<K, V>();
    
                for (Map.Entry<?, ?> entry : ((Map<?, ?>) object).entrySet()) {
                    result.put(keyType.cast(entry.getKey()),
                            valueType.cast(entry.getValue()));
                }
    
                return result;
            }
    
            throw new ClassCastException();
        }
    
        public static <K, V> List<Map<K, V>> castMapList(Object object,
                Class<K> keyType, Class<V> valueType) {
    
            if (object == null) {
                return null;
            }
    
            if (object instanceof List<?>) {
                List<Map<K, V>> result = new ArrayList<Map<K, V>>();
    
                for (Object o : (List<?>) object) {
                    result.add(castMap(o, keyType, valueType));
                }
    
                return result;
            }
    
            throw new ClassCastException();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-13
      • 1970-01-01
      • 1970-01-01
      • 2013-12-21
      • 1970-01-01
      • 2011-05-27
      • 1970-01-01
      相关资源
      最近更新 更多