取消选中从对象到列表的强制转换

时间:2016-03-15 20:55:09

标签: java generics

我正在尝试为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&lt; Map&gt;转换列出&lt; Map&lt; String,Object&gt;&gt;。有没有办法扩展castList来做到这一点?

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

我尝试了一个特定于地图列表的修改版本,但仍然出现错误&#34;方法castMapList(Object,Class&lt; Map&lt; K,V&gt;&gt;)不适用于参数  (对象,类&lt;地图&gt;)&#34;这似乎是同一错误的不同风格。

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;
}

3 个答案:

答案 0 :(得分:2)

由于删除,有:

  1. 无法拥有Class<Map<K, V>>
  2. 没有内置方法可以在运行时检查每个特定Map是否有某些特定KV
  3. 那是:

    1. 正如您所发现的,每个泛型类都有一个对应的原始类型Class对象,例如Map.classClass<Map>
    2. obj instanceof Map<K, V>这样的东西是编译器错误而Class个对象不检查通用参数,例如isInstancecast
    3. Point-being,如果没有额外的工作,你无法在运行时验证泛型。

      您仍然可以围绕它进行编程,具体取决于您正在做什么。例如,一种简单的方法就是重建每个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

      之类的东西

答案 1 :(得分:0)

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

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.java来自public sealed class ExpandoObject : IDynamicMetaObjectProvider, IDictionary<string, object>, ICollection<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>, IEnumerable, INotifyPropertyChanged ,他们也会这样做。您必须确保该列表包含地图。

答案 2 :(得分:0)

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

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();
    }
}