使用方法将原始地图投射到通用地图,以早期失败的方式干净安全地 [英] Cast a raw map to a generic map using a method, cleanly and safely in a fail early manner
问题描述
投射, 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 returningHashMap<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屋!