使用方法将原始地图投射到通用地图,以早期失败的方式干净安全地 [英] Cast a raw map to a generic map using a method, cleanly and safely in a fail early manner

查看:274
本文介绍了使用方法将原始地图投射到通用地图,以早期失败的方式干净安全地的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

投射 instanceof @ SuppressWarnings(未选中)是嘈杂。这将是很高兴,他们下降到一个方法,他们不需要看看。 CheckedCast.castToMapOf()尝试这样做。

Casting, instanceof, and @SuppressWarnings("unchecked") are noisy. It would be nice to stuff them down into a method where they won't need to be looked at. CheckedCast.castToMapOf() is an attempt to do that.

castToMapOf / code>正在做出一些假设:

castToMapOf() is making some assumptions:


  • (1)地图不能被信任为同质

  • (2)重新设计为避免需要投射或实例化是不可行的

  • (3)确保类型安全在失败早期方式比性能影响更重要

  • (4)返回 Map< String,String> (5)键和值类型args不是通用的(例如,键和值类型),而不是返回 HashMap HashMap< String,ArrayList< String>>

  • (1) The map can't be trusted to be homogeneous
  • (2) Redesigning to avoid need for casting or instanceof is not viable
  • (3) Ensuring type safety in an fail early manner is more important than the performance hit
  • (4) Returning Map<String,String> is sufficient (rather than returning HashMap<String, String>)
  • (5) The key and value type args are not generic (like HashMap<String, ArrayList<String>>)

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

(1), (2) and (3) are symptoms of my work environment, beyond my control. (4) and (5) are compromises I've made because I haven't found good ways to overcome them yet.

(4)如果 HashMap.class 被传递给类< M> ,我无法弄清楚如何返回 M 。所以我返回一个地图< K,V>

(4) Is difficult to overcome because even if a HashMap.class was passed into a Class<M> I haven't been able to figure out how to return a M<K, V>. So I return a Map<K, V>.

(5) code> Class< T> 。我很愿意听到替代的想法。

(5) Is probably an inherent limitation of using Class<T>. I'd love to hear alternative ideas.

尽管有这些限制,你会看到这个代码的任何问题吗?我做任何假设我还没有确定?有没有更好的方法来做到这一点?如果我重新发明轮子,请指点我的轮子。 :)

Despite those limitations can you see any problems with this code? Am I making any assumptions I haven't identified? Is there a better way to do this? If I'm reinventing the wheel please point me to the wheel. :)

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)

    }
}

我发现一些阅读有帮助:

Some reading I found helpful:

未知实现类的通用工厂

通用和参数化类型

我也想知道 TypeReference / 超级类型令牌可能有助于(4)和(5),并且是一个更好的方法来处理这个问题。

I'm also wondering if a TypeReference / super type tokens might help with (4) and (5) and be a better way to approach this problem. If you think so please post an example.

推荐答案

代码看起来不错,但我想补充一个假设:原始引用将不再使用。因为如果你把 Map 转换为 Map< String,String> 你可能会得到惊喜。

The code looks good, but I would add an assumption: (6) the raw reference will never be used anymore. Because if you cast your Map to a Map<String, String>, then put an integer to the raw map, you may get surprises.

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

而不是投射地图,我会创建一个新的$ c> LinkedHashMap 以保留顺序),在将每个对象添加到新地图时投射它们。这样, ClassCastException 将被自然抛出,旧的地图引用仍然可以修改,而不会影响新的。

Instead of casting the map, I would create a new one (maybe a LinkedHashMap to preserve order), casting each object as you add them to the new map. This way, the ClassCastException would be thrown naturally, and the old map reference can still be modified without affecting the new one.

这篇关于使用方法将原始地图投射到通用地图,以早期失败的方式干净安全地的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆