我很难理解Groovy的类型和类型提升,以及Groovy的@TypeChecked
annotation的确切承诺。
--或者,我可能无法理解Groovy的设计理念。
我正在尝试@TypeChecked注解,它的行为并不像预期的那样。我做了两个示例脚本,我预计它们都会因为类型不匹配而失败。但只有一个脚本失败了。
这两个脚本非常相似。所以我认为它们的行为方式也是相似的。主要的区别是接近顶部的地方:我要么把x声明为int,要么声明为String,然后给x赋值一个不同的类型。
脚本差异:
$ diff TypeChecked-fail-int-x.groovy TypeChecked-pass-String-x.groovy -y --width 70
@groovy.transform.TypeChecked @groovy.transform.TypeChecked
void m(){ void m(){
int x | String x
x = 123 | x = "abc"
println(x) println(x)
println(x.getClass()) println(x.getClass())
println() println()
x = "abc" | x = 123
println(x) println(x)
println(x.getClass()) println(x.getClass())
} }
m() m()
当我声明一个变量为int,但随后试图赋值一个String时,我会得到预期的错误:
脚本TypeChecked-fail-int-x.groovy
:(Groovy Web控制台here。)
@groovy.transform.TypeChecked
void m(){
int x
x = 123
println(x)
println(x.getClass())
println()
x = "abc"
println(x)
println(x.getClass())
}
m()
输出:
$ groovy --version
Groovy Version: 3.0.10 JVM: 11.0.17 Vendor: Ubuntu OS: Linux
$ groovy TypeChecked-fail-int-x.groovy
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
/home/myuser/TypeChecked-fail-int-x.groovy: 11: [Static type checking] - Cannot assign value of type java.lang.String to variable of type int
@ line 11, column 9.
x = "abc"
^
1 error
然而:如果我以相反的方式来做,那么它运行得很好。我希望类型检查器也能捕捉到这一点。
脚本TypeChecked-pass-String-x.groovy
:(Groovy Web控制台here。)
@groovy.transform.TypeChecked
void m(){
String x
x = "abc"
println(x)
println(x.getClass())
println()
x = 123
println(x)
println(x.getClass())
}
m()
输出:
$ groovy TypeChecked-pass-String-x.groovy
abc
class java.lang.String
123
class java.lang.String
它不仅运行了,而且突然间int 123
变成了String "123"
!
我预计两个脚本都将失败。
我也试了X1 E3 F1 X,结果是一样的。
问题:
- 这是预期的行为还是一个bug?来源?
- 为什么123现在是一个字符串?是否有一些自动装箱/转换/类型提升正在进行?我可以停止吗?
1条答案
按热度按时间w41d8nur1#
您在Groovy中遇到了两个概念,静态类型检查(TypeChecked)和流类型。
类型已选中
TypeChecked
有所谓的“类型检查赋值”规则。类型A的对象o可以赋值给类型T的变量当且仅当:
例如,如果您将初始的
String
类型更改为Boolean
,您也会感到惊讶,但这将符合Groovy规范,输出将是:如果您想知道为什么输出有两个
true
值,您可能需要阅读Groovy Truth。流类型
流类型化是Groovy在类型检查模式下的一个重要概念,也是类型推理的一个扩展,其思想是编译器能够在代码流中推理变量的类型,而不仅仅是在初始化时。
我们还对该链接的另一个声明感兴趣:
重要的是要理解,使用def声明变量并不会触发类型推断。流类型化适用于任何类型的任何变量。使用显式类型声明变量只会约束可以为变量分配的内容。
因此,如果将初始变量类型从
String
更改为def
,则会看到不同的结果:当初始类型为
String
时,它将始终是String
变量,您可以根据“类型检查赋值”规则将任何类型的对象赋值给String变量。摘要
所以,回答你的问题:
这是预期的行为还是一个bug?来源?
是的,这是预期的。请参考上面的链接和说明。
为什么123现在是一个字符串?是否有一些自动装箱/转换/类型提升正在进行?我可以停止吗?
同样,请参考上面的链接和解释。如果你想改变变量的类型,那么通过
def
关键字定义变量。你不能停止这个过程,因为(再次声明文档):引入流类型化是为了减少经典Groovy和静态Groovy之间的语义差异。