我想创建一个通用函数,可以使用任何Map&一个字符串键,如果映射中没有键,那么它应该创建一个值类型的新实例(传递)&把它放在地图上然后归还。
这是我的实施
public <T> T getValueFromMap(Map<String, T> map, String key, Class<T> valueClass){
T value = map.get(key);
if (value == null){
try {
value = valueClass.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
map.put(key, value);
}
return value;
}
如果我将它与普通(非通用)List一起用作值类型
,它就可以工作Map<String,List> myMap;
List value = getValueFromMap(myMap, "aKey", List.class) //works
但不是通用类型列表
Map<String,List<String>> myMap;
List<String> value = getValueFromMap(myMap, "aKey", List.class) //does not work
此外,如果我尝试将Map<String, T>
地图参数更改为Map<String, T<?>>
,则会抱怨the type T is not generic; it cannot be parameterized with arguments <?>
通用参数本身可以是通用的吗?
有没有办法创建具有上述要求的功能?
〜更新
感谢大家的所有见解。
我已验证所接受的解决方案适用于使用此代码的任何值类型
Map<String, Map<String, List<String>>> outer =
new HashMap<String, Map<String,List<String>>>();
Map<String, List<String>> inner = getValueFromMap(outer, "b",
(Class<Map<String, List<String>>>)(Class<?>)HashMap.class);
List<String> list = getValueFromMap(inner, "a",
(Class<List<String>>)(Class<?>)ArrayList.class);
答案 0 :(得分:5)
您需要大多数特定类型,String
。在您需要特定类型的情况下,List.class
会向您返回Class<List<?>>
,因此您需要添加广告Class<List<String>>
Map<String,List<String>> myMap = new HashMap<String, List<String>>();;
List<String> value = getValueFromMap(myMap, "aKey",
(Class<List<String>>)(Class<?>)ArrayList.class) ;
现在,如果你调用下面的方法
getValueFromMap(myMap, "aKey", List.class)
^^^T is List<String> ^^^ T is List<?> wild char since List.class
will return you Class<List<?>>
因此,您对T
的定义会导致编译器混淆,从而导致编译错误。
您无法创建List.class
的实例,因为它是您需要实现类的接口,因此使用ArrayList.class
调用方法
答案 1 :(得分:1)
第一个示例是因为您使用的是未建议的原始类型。
要解决编译错误,您可能不希望将类限制为T但允许某些超级T
public <T> T getValueFromMap(Map<String, T> map, String key, Class<? super T> valueClass)
但是你将无法保存转换,你需要将新实例的结果转换为(T)。并且你不能使用List作为类,因为它是一个iterface,你不能创建它的实例。
public <K,V> V getValueFromMap(Map<K, V> map, K key, Class<? super V> valueClass){
if(map.containsKey(key) == false) {
try {
map.put(key, (V) valueClass.newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return map.get(key);
}
Map<String,ArrayList<String>> myMap = new HashMap<String, ArrayList<String>>();
List<String> value = getValueFromMap(myMap, "aKey", ArrayList.class); //will work
修改强>
但OP期望的基本形式Map<String,List<String> myMap
呢?
我们应该知道ArrayList
是List
的超级类型,但ArrayList<String>
不是List<String>
的超级类型,但它是可分配的。
让我们试一试:
示例1:getValueFromMap(myMap, "aKey", ArrayList.class); //Compilation error
示例2:getValueFromMap(myMap, "aKey", List.class); //Runtime error
对此的解决方案可能是假设如果class是List则创建ArrayList。
这迫使我们创建一些不合适的代码。
public static <V> V createInstace(Class<V> valueClass) throws InstantiationException, IllegalAccessException {
if(List.class.isAssignableFrom(valueClass)) {
return (V) new ArrayList<V>();
}
return valueClass.newInstance();
}
这段代码与泛型有任何共同之处但是有效。
最后我们以
结束public static void main(String[] args) {
Map<String,List<String>> myMap = new HashMap<String, List<String>>();
List<String> value = getValueFromMap(myMap, "aKey", List.class); //does not work
value.add("Success");
System.out.println(value);
}
public static <K,V> V getValueFromMap(Map<K, V> map, K key, Class<? super V> valueClass){
if(map.containsKey(key) == false) {
try {
map.put(key, (V) createInstace(valueClass));
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return map.get(key);
}
public static <V> V createInstace(Class<V> valueClass) throws InstantiationException, IllegalAccessException {
if(List.class.isAssignableFrom(valueClass)) {
return (V) new ArrayList<V>();
}
return valueClass.newInstance();
}
结果如果是[Sucess]
。
答案 2 :(得分:1)
由于泛型类型信息在运行时被删除,因此无法使用具有通用类型信息的参数调用泛型方法。
请参阅http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ004
如果您总是使用列表地图,那么您可以声明采用Map<String, List<T>>
的方法
答案 3 :(得分:1)
这里的问题是你要求Class<T>
参数,并且无法直接获得Class<List<String>>
个对象。
List.class
为您提供Class<List>
,但List<String>.class
甚至无法编译(在语法上不正确)。
解决方法是将类对象自己转换为通用版本。你需要先进行upcast,否则Java会抱怨这些类型不可分配:
Class<List<String>> clz = (Class<List<String>>)(Class<?>)List.class;
由于泛型是通过擦除实现的,因此转换不会从根本上做任何事情 - 但它使编译器对T
的推断感到满意,并允许像你这样灵活的通用方法工作。当您想要传递Class<T>
令牌时,这不是一种不常见的解决方法。