java - 使用一种方法,以失败早期的方式干净、安全地将原始 map 转换为通用 map

标签 java generics casting java-5

Casting , instanceof ,和@SuppressWarnings("unchecked")很吵。如果将它们塞进一种不需要查看的方法中,那就太好了。 CheckedCast.castToMapOf()是这样做的尝试。

castToMapOf()正在做出一些假设:

  • (1) map 不能被信任为 homogeneous
  • (2) 重新设计为 avoid need for casting or instanceof不可行
  • (3) 确保 fail early 中的类型安全方式比性能影响更重要
  • (4) 返回Map<String,String>就足够了(而不是返回 HashMap<String, String> )
  • (5) 键和值类型参数不是通用的(如 HashMap<String, ArrayList<String>> )

(1)、(2) 和 (3) 是我工作环境的症状,超出了我的控制范围。 (4) 和 (5) 是我做出的妥协,因为我还没有找到克服它们的好方法。

(4) 很难克服,因为即使 HashMap.class被传递到 Class<M>我一直不知道如何返回 M<K, V> 。所以我返回一个 Map<K, V> .

(5) 可能是使用 Class<T> 的固有限制。我很想听听其他想法。

尽管有这些限制,您能看出这段代码有什么问题吗?我是否做出了任何我尚未确定的假设?有一个更好的方法吗?如果我要重新发明轮子,请给我指出轮子。 :)

public class CheckedCast {

    public static final String LS = System.getProperty("line.separator");

    /** Check all contained items are claimed types and fail early if they aren't */
    public static <K, V> Map<K, V> castToMapOf( 
            Class<K> clazzK,    
            Class<V> clazzV,
            Map<?, ?> map) {

        for ( Map.Entry<?, ?> e: map.entrySet() ) {
            checkCast( clazzK, e.getKey() );            
            checkCast( clazzV, e.getValue() );            
        }

        @SuppressWarnings("unchecked")
        Map<K, V> result = (Map<K, V>) map;        
        return result; 
    }

    /** Check if cast would work */
    public static <T> void checkCast(Class<T> clazz, Object obj) {
        if ( !clazz.isInstance(obj) ) {
            throw new ClassCastException(
                LS + "Expected: " + clazz.getName() +
                LS + "Was:      " + obj.getClass().getName() +
                LS + "Value:    " + obj
            );
        }
    }

    public static void main(String[] args) {

        // -- Raw maps -- //

        Map heterogeneousMap = new HashMap();
        heterogeneousMap.put("Hmm", "Well");
        heterogeneousMap.put(1, 2); 

        Map homogeneousMap = new HashMap();
        homogeneousMap.put("Hmm", "Well");

        // -- Attempts to make generic -- //

        //Unsafe, will fail later when accessing 2nd entry
        @SuppressWarnings("unchecked") //Doesn't check if map contains only Strings
        Map<String, String> simpleCastOfHeteroMap = 
                    (Map<String, String>) heterogeneousMap;  

        //Happens to be safe.  Does nothing to prove claim to be homogeneous.
        @SuppressWarnings("unchecked") //Doesn't check if map contains only Strings
        Map<String, String> simpleCastOfHomoMap = 
                    (Map<String, String>) homogeneousMap;  

        //Succeeds properly after checking each item is an instance of a String
        Map<String, String> checkedCastOfHomoMap = 
                    castToMapOf(String.class, String.class, homogeneousMap);

        //Properly throws ClassCastException
        Map<String, String> checkedCastOfHeteroMap = 
                    castToMapOf(String.class, String.class, heterogeneousMap); 
        //Exception in thread "main" java.lang.ClassCastException: 
        //Expected: java.lang.String
        //Was:      java.lang.Integer
        //Value:    1
        //    at checkedcast.CheckedCast.checkCast(CheckedCast.java:14)
        //    at checkedcast.CheckedCast.castToMapOf(CheckedCast.java:36)
        //    at checkedcast.CheckedCast.main(CheckedCast.java:96)

    }
}

我发现一些有用的阅读:

Generic factory with unknown implementation classes

Generic And Parameterized Types

我还想知道是否 TypeReference/super type tokens可能对(4)和(5)有帮助,并且是解决这个问题的更好方法。如果您这么认为,请发布一个示例。

最佳答案

代码看起来不错,但我会添加一个假设:(6) 原始引用将永远不会再被使用。因为如果你投 MapMap<String, String> ,然后将一个整数放入原始映射中,你可能会得到惊喜。

Map raw = new HashMap();
raw.put("Hmm", "Well");
Map<String, String> casted = castToMapOf(String.class, String.class, raw); // No error
raw.put("one", 1);
String one = casted.get("one"); // Error

我不会创建一个新的 map (可能是 LinkedHashMap 以保持顺序),而是在将每个对象添加到新 map 时对其进行转换,而不是转换 map 。这样,ClassCastException会自然抛出,并且仍然可以修改旧 map 引用而不影响新 map 引用。

关于java - 使用一种方法,以失败早期的方式干净、安全地将原始 map 转换为通用 map ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25618452/

相关文章:

java - 不带注解的 Autowiring @Autowired

java - java中的提升和转换

java - doGet 和 doPost 的 HttpServlet 契约

java - 提供 MySQL JDBC 驱动程序密码的最佳实践

Java - 如何在泛型类中调用我的类 T 的方法

java - 如何访问传递的通用数组列表中的元素

java - 一种创建接口(interface)扩展比较器接口(interface)的方法,该接口(interface)仅比较当前子类

c - 未初始化指针的可能输出

java - 将泛型集合转换为数组

java - 可以向@ManyToMany Hibernate 额外表添加额外字段吗?