为什么Groovy @TypeChecked注解会捕捉到我将String放入int变量中,而不是相反?

dy1byipe  于 2022-11-28  发布在  其他
关注(0)|答案(1)|浏览(190)

我很难理解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现在是一个字符串?是否有一些自动装箱/转换/类型提升正在进行?我可以停止吗?
w41d8nur

w41d8nur1#

您在Groovy中遇到了两个概念,静态类型检查(TypeChecked)和流类型。

类型已选中

TypeChecked有所谓的“类型检查赋值”规则。
类型A的对象o可以赋值给类型T的变量当且仅当:

  • T等于A
  • 或者T是字符串、布尔值、布尔值或类之一**(这一个与问题最相关)**
  • ...

例如,如果您将初始的String类型更改为Boolean,您也会感到惊讶,但这将符合Groovy规范,输出将是:

true
class java.lang.Boolean

true
class java.lang.Boolean

如果您想知道为什么输出有两个true值,您可能需要阅读Groovy Truth。

流类型

流类型化是Groovy在类型检查模式下的一个重要概念,也是类型推理的一个扩展,其思想是编译器能够在代码流中推理变量的类型,而不仅仅是在初始化时。
我们还对该链接的另一个声明感兴趣:
重要的是要理解,使用def声明变量并不会触发类型推断。流类型化适用于任何类型的任何变量。使用显式类型声明变量只会约束可以为变量分配的内容。
因此,如果将初始变量类型从String更改为def,则会看到不同的结果:

abc
class java.lang.String

123
class java.lang.Integer

当初始类型为String时,它将始终是String变量,您可以根据“类型检查赋值”规则将任何类型的对象赋值给String变量。

摘要

所以,回答你的问题:
这是预期的行为还是一个bug?来源?
是的,这是预期的。请参考上面的链接和说明。
为什么123现在是一个字符串?是否有一些自动装箱/转换/类型提升正在进行?我可以停止吗?
同样,请参考上面的链接和解释。如果你想改变变量的类型,那么通过def关键字定义变量。你不能停止这个过程,因为(再次声明文档):
引入流类型化是为了减少经典Groovy和静态Groovy之间的语义差异。

相关问题