
Apache Groovy 中的变量:强大、简洁且易于使用
Groovy 是一种强大、简洁且易于学习的语言; 自 2008 年以来我就爱上了它。我通过这个 系列 旨在展示它能为您做些什么。(如果您尚未安装 Groovy,请 阅读介绍。)

为什么现在在第六篇文章中讨论变量? 我保证接下来会有有趣的内容。
首先编写一个非常简单的脚本,声明一个整数,设置其值,打印出来并打印出其类
1 int s
2 s = 42
3 println s
4 println s.getClass()
现在运行脚本
$ groovy Groovy05a.groovy
42
class java.lang.Integer
$
请注意,尽管将变量 s
声明为 int
类型,但它似乎实际上是 java.lang.Integer
类的一个实例。 实际上就是这种情况——Groovy 中看起来像是原始类型的东西(int
、float
、double
、boolean
)根本不是原始类型。 它们是类,就像 String
或 Date
一样。
您也知道您不需要指定变量 s
的类型。 您可以使用 def
关键字来声明它,如下面的脚本所示
1 def s
2 s = 42
3 println s
4 println s.getClass()
运行此脚本并查看会发生什么
$ groovy Groovy05b.groovy
42
class java.lang.Integer
$
因此,似乎将 42
分配给上面的 s
会以某种方式使 s
成为 java.lang.Integer
的实例。 但那是对的吗? 尝试另一个实验
1 def s
2 s = 42
3 println s
4 println s.getClass()
5 s = “The meaning of everything”
6 println s
7 println s.getClass()
等等,您真的认为这会奏效吗? 好吧,试试看
$ groovy Groovy05c.groovy
42
class java.lang.Integer
The meaning of everything
class java.lang.String
$
好的,所以似乎您可以自由使用 s
来保存任何类的实例。 但是,如果您回到显式键入 s
会发生什么?
1 int s
2 s = 42
3 println s
4 println s.getClass()
5 s = "The meaning of everything"
6 println s
7 println s.getClass()
运行它
$ groovy Groovy05d.groovy
42
class java.lang.Integer
Caught: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object ‘The meaning of everything’ with class ‘java.lang.String’ to class ‘int’
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object ‘The meaning of everything’ with class ‘java.lang.String’ to class ‘int’
at Groovy05d.run(Groovy05d.groovy:5)
$
因此,在其声明中显式键入变量或仅使用 def
之间存在很大差异。 很高兴知道这一点。 想象一下,如果您声明一个变量,您打算保存年份值,后来您(或其他人)感到困惑并尝试将日期分配给该变量。 您可以放心地知道,只要您将变量声明为 int
,就不能将 Date
值分配给它。
唯一的陷阱——总会有一个——是将某物声明为 String,然后稍后为其分配 int
值。 不起作用,对吧? 好吧,它有点起作用——大多数 Groovy 类都有一个 toString()
方法,可以自动“转换”为 String 值。
例如
1 String s
2 s = 42
3 println s
4 println s.getClass()
5 s = “The meaning of everything”
6 println s
7 println s.getClass()
它在运行时产生这个
$ groovy Groovy05e.groovy
42
class java.lang.String
The meaning of everything
class java.lang.String
$
在这里,您可以看到整数值 42
已转换为字符串。 这可能会产生一些奇怪的错误,例如
1 String s
2 s = 42
3 println s
4 println s.getClass()
5 s *= 2
6 println s
7 println s.getClass()
看看第五行,您“忘记” s
是一个字符串,并尝试将其乘以 2? 运行这个
$ groovy Groovy05f.groovy
42
class java.lang.String
4242
class java.lang.String
$
果然,42 * 2 = 4242。
这是一个很酷的想法:如果您根本不声明 s
会怎么样? 让我们试试看
1 s = 42
2 println s
3 println s.getClass()
4 s *= 2
5 println s
6 println s.getClass()
Groovy 应该会抱怨,对吧? 运行这个
$ groovy Groovy05g.groovy
42
class java.lang.Integer
84
class java.lang.Integer
$
第一行中的 s
来自哪里? 事实证明,Groovy 脚本有这个叫做“绑定”的东西——具体来说,总是有一个 groovy.lang.Binding
的实例,称为 binding
,它在任何 Groovy 脚本中都被隐式声明和可用。 任何未声明的变量都会出现在绑定中。 这个脚本演示了它
1 s = 42
2 println s
3 println s.getClass()
4 println binding.getVariables()
运行它
$ groovy Groovy05h.groovy
42
class java.lang.Integer
[args:[], s:42]
$
请注意其中的变量 s
及其值 42
。 您还应该注意到变量 args
——这是命令行上的参数来自 Groovy 脚本内部的位置。 最后,请注意 getVariables()
方法返回变量及其值的映射。
最后,我赞赏 Groovy 为使用 Binding
实例提供的一些不错的语法支持
1 s = 42
2 println s
3 println “binding.getVariable(‘s’) ${binding.getVariable(‘s’)}”
4 println “binding.getProperty(‘s’) ${binding.getProperty(‘s’)}”
5 println “binding.s ${binding.s}”
6 println “binding[‘s’] ${binding[‘s’]}”
运行它
$ groovy Groovy05i.groovy
42
binding.getVariable(‘s’) 42
binding.getProperty(‘s’) 42
binding.s 42
binding[‘s’] 42
$
看到所有这些访问“变量” s
的不同方式的等价性了吗?
绑定是 Groovy 提供的一种很酷的机制,允许从外部将有用的东西注入到脚本中。 当在 Groovy 中制作特定领域语言时,它尤其有用。
结论
事实证明,在 Groovy 脚本中声明变量——甚至更重要的是,不声明变量——是一个具有一定深度的主题。 以我的经验来看,对于任何我希望长期维护的代码,最好声明变量的类型,而不是使用 def
关键字。 另外,请注意声明为 String
的变量。 最后,我敦促您深入研究绑定,因为它们是使 Groovy 变得 groovy 的原因之一!
照片由 Alina Grubnyak 拍摄于 Unsplash
留下评论