收集方法上的Groovy闭包不一致的类型转换
Groovy中存在奇怪的行为。请看下面的两个示例:收集方法上的Groovy闭包不一致的类型转换,groovy,spock,Groovy,Spock,Groovy中存在奇怪的行为。请看下面的两个示例: def list = [[BigDecimal.ONE]] list.each { println it.class } 印刷品: 类java.util.ArrayList 及 印刷品: 类java.math.BigDecimal 示例中唯一的区别是,第二个示例具有指定闭包的参数类型。但这并不能解释为什么以及如何将内部列表转换为BigDecimal。我宁愿期望ClassCastException。此外,这种行为是不一致的,好像内部列表
def list = [[BigDecimal.ONE]]
list.each {
println it.class
}
印刷品:
类java.util.ArrayList
及
印刷品:
类java.math.BigDecimal
示例中唯一的区别是,第二个示例具有指定闭包的参数类型。但这并不能解释为什么以及如何将内部列表
转换为BigDecimal
。我宁愿期望ClassCastException
。此外,这种行为是不一致的,好像内部列表中有更多的元素,它会失败,导致MissingMethodException
我们发现这种神奇的类型转换发生在(第256行)
这是一个设计的行为还是一个bug
编辑:
我在尝试使用Spock存根方法时遇到了上述问题。该方法将集合
作为参数。考虑另一个例子:
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]
问题是参数它
作为List
嵌入到另一个List
方法存根闭包中。WTF
克服这一问题的唯一方法是使用与输入类型完全相同的参数类型的stubing方法
listStub.addAll(input) >> { ArrayList it ->
…然后测试通过。这是一个真正的禁忌,因为我需要使用接口作为存根参数类型,而不是一个特定的实现。当它被宣布为
listStub.addAll(input) >> { List it ->
或
…由于输入列表嵌入到另一个列表中,因此与不使用类型时失败的方式相同
如果您想运行和使用它,下面是groovy解构提供给闭包的项(最好的例子是
映射中的每个项,其中传递键和值)。因此,它在一致的使用上是一致的:
[[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
谢谢你的解释。我可以通过Map
了解这种行为的原因,而且我经常使用它。但是,使用集合
可能会导致意外的应用程序行为。无论如何,你能在我的原始问题的“编辑”部分下看一看并解释这个谜团吗?很抱歉,我无法帮助解决斯波克问题,而且这甚至会产生一个新问题(例如,彼得有机会提供完整的答案)谢谢,我不确定这是不是同一个问题。事实上并不完全相同,但它们是相关的。我正考虑把它作为一个新问题来问,也许我应该问。capturedArguments的类型是:java.util.Arrays$ArrayList
,因为DefaultGroovyMethods.java
:公共静态布尔addAll(集合self,T[]项){return self.addAll(Arrays.asList(项));}
。一切正常。如果显式指定参数,它将自动转换,并且不会引发异常。请参阅:在Spock模拟响应中,it
或单个非类型化闭包参数将包含方法的参数列表。如果改用类型化闭包参数,Spock将尝试根据Groovy语义进行结构分解。看,谢谢你,彼得。我现在明白了为什么我的输入列表被嵌入到另一个非类型化Spock模拟响应参数的列表中。但为什么即使参数是类型化的,但使用父类或接口,也会发生同样的情况呢?当然,我会再看一遍文档,但如果你能多说几句话,那就太棒了。下面是我想问的一个例子,这种方法需要多次删除同一个方法,比如:LinkedList、ArrayList、Set等等。。。这看起来不太好,即使CodeNarc抱怨ImplementationAsTypeDestructuring将(仅)在闭包参数无法按闭包声明的参数类型传递时启动。(与我之前所说的相反,所有这些都在Groovy的控制之下,因此遵循标准Groovy语义。Spock只使用一个List
类型的参数调用闭包,表示存根调用的参数列表。)换句话说,当使用类型为Iterable
、Collection
或List
的单个参数存根一个方法时,您必须手动分解结构({args->args[0]}
或>{it[0]}
)。^^^最后一句话最好放在文档中。
listStub.addAll(input) >> { Collection it ->
[[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