Groovy 集合方法闭包不一致的类型转换
Groovy closure-on-collection-methods inconsistent type transformation
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]
问题是参数 it
作为 List
嵌入另一个 List
到方法存根闭包中。 WTF?
克服这个问题的唯一方法是使用 与输入类型 完全相同的参数类型 like
listStub.addAll(input) >> { ArrayList it ->
...然后测试通过。这是一个真正的禁忌,因为我需要使用接口作为存根参数类型,而不是特定的实现。当它被声明为
listStub.addAll(input) >> { List it ->
或
listStub.addAll(input) >> { Collection it ->
...它失败的方式与没有类型相同,因为 input 列表被嵌入到另一个列表中。
这里是 live example 如果你喜欢运行 玩一下
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
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]
问题是参数 it
作为 List
嵌入另一个 List
到方法存根闭包中。 WTF?
克服这个问题的唯一方法是使用 与输入类型 完全相同的参数类型 like
listStub.addAll(input) >> { ArrayList it ->
...然后测试通过。这是一个真正的禁忌,因为我需要使用接口作为存根参数类型,而不是特定的实现。当它被声明为
listStub.addAll(input) >> { List it ->
或
listStub.addAll(input) >> { Collection it ->
...它失败的方式与没有类型相同,因为 input 列表被嵌入到另一个列表中。
这里是 live example 如果你喜欢运行 玩一下
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