使用withDefault创建Map时,在放置元素时会导致null

时间:2014-07-14 14:30:08

标签: groovy

我正在尝试使用Groovy方法创建具有默认值的TreeMap<String, List<Data>>,因此如果密钥尚未存在,我可以轻松地将数据添加到新列表中。

TreeMap<String, List<Data>> myData = (TreeMap<String, List<Data>>) [:].withDefault { [] }

如您所见,我要求使用TreeMapwithDefault只返回Map个实例,因此我需要进行投射。

当我尝试向地图添加新列表时,

myData[newKey].add(newData)

myData[newKey]null。但是,如果我更改Map初始化以删除TreeMap强制转换(并将类型更改为Map而不是TreeMap),myData[newKey].add(newData)将按预期工作。

这是什么原因?如果我投射地图,我可以不使用withDefault吗?

1 个答案:

答案 0 :(得分:2)

问题不在于演员。它还与声明的类型有关。问题可以简化为:

def map1 = [:].withDefault { 0 }
TreeMap map2 = map1

执行该操作时map1groovy.lang.MapWithDefault的实例,map2java.util.TreeMap的实例。它们是堆上的2个独立对象,而不仅仅是指向同一对象的2个引用。 map2不会有任何与之关联的默认行为。就好像你这样做了:

def map1 = [:].withDefault { 0 }
TreeMap map2 = new TreeMap(map1)

这就是您的代码所发生的事情。演员和泛型只是让你的代码不那么清晰。

此:

TreeMap<String, List<Data>> myData = (TreeMap<String, List<Data>>) [:].withDefault { [] }

可以分解为:

def tmpMap = [:].withDefault { [] }
TreeMap<String, List<Data>> myData = (TreeMap<String, List<Data>>)tmpMap

我希望有所帮助。

修改

另一种看待同样事情发生的方法是做这样的事情:

Set names = new HashSet()
ArrayList namesList = names

当第二行执行时,创建一个新的ArrayList,就好像你已经完成ArrayList namesList = new ArrayList(names)一样。这看起来与您在代码中的内容不同,但同样的事情正在发生。您有一个与它关联的静态类型的引用,并将该引用指向不同类型的对象,Groovy正在创建您声明的类型的实例。在上面这个简单的例子中,声明的类型是ArrayList。在您的示例中,声明的类型为TreeMap<String, List<Data>>