Groovy closure-on-collection-methods不一致的类型转换

时间:2015-01-05 13:17:02

标签: groovy spock

Groovy中有一些奇怪的行为。看看下面的两个例子:

def list = [[BigDecimal.ONE]]
list.each {
    println it.class
}

打印:

  

class java.util.ArrayList

def list = [[BigDecimal.ONE]]
list.each { BigDecimal it ->
    println it.class
}

打印:

  

class java.math.BigDecimal

示例中唯一的区别是第二个具有指定闭包的参数类型。但这并不能解释为什么以及内部List如何转变为BigDecimal。我宁愿期待ClassCastException。此外,这种行为是不一致的,好像内部列表中有更多元素失败MissingMethodException

我们发现这种神奇的类型转换发生在ClosureMetaClass(第256行)

是设计行为还是错误?

修改 我在尝试使用Spock存根方法时遇到了上述问题。该方法将Collection作为参数。考虑另一个例子:

def 'stub a method with collection as argument'() {
    given:
    def input = [1, 2, 3]
    def capturedArgument
    List listStub = Stub()
    listStub.addAll(input) >> {
        capturedArgument = it
    }

    when:
    listStub.addAll(input)

    then:
    input.class == capturedArgument.class
}

失败了:

Condition not satisfied:

input.class == capturedArgument.class
|     |     |  |                |
|     |     |  [[1, 2, 3]]      class java.util.Arrays$ArrayList
|     |     false
|     class java.util.ArrayList
[1, 2, 3]

问题是参数itList嵌入到另一个List中的方法存根闭包中。 WTF?

解决这个问题的唯一方法是使用与输入类型完全相同的参数类型的存根方法

listStub.addAll(input) >> { ArrayList it ->

...然后测试通过。这是一个真正的禁忌,因为我需要使用接口作为存根参数类型,而不是特定的实现。当它被宣布为

listStub.addAll(input) >> { List it ->

listStub.addAll(input) >> { Collection it ->

...它的失败方式与没有类型的失败相同,因为 input 列表嵌入在另一个列表中。

如果你喜欢跑步和玩耍,那么live example

1 个答案:

答案 0 :(得分:2)

groovy解构提供给闭包的项目(最好的例子是每个Map,其中传递了键和值)。所以它在一致使用方面是一致的:

[[BigDecimal.ONE],[BigDecimal.ONE]].each{ BigDecimal it -> println it } 
//=> 1
//=> 1
[[BigDecimal.ONE, BigDecimal.ONE]].each{ a, b -> println "$a and $b" }
//=> 1 and 1