脚本与类
Groovy代码文件与class文件的对应关系
作为基于JVM的语言,Groovy可以非常容易的和Java进行互操作,但也需要编译成class文件后才能运行,所以了解Groovy代码文件和class文件的对应关系,有助于更好地理解Groovy的运行方式和结构。
对于没有任何类定义
如果Groovy脚本文件里只有执行代码,没有定义任何类(class),则编译器会生成一个Script的子类,类名和脚本文件的文件名一样,而脚本的代码会被包含在一个名为run的方法中,同时还会生成一个main方法,作为整个脚本的入口。
对于仅有一个类
如果Groovy脚本文件里仅含有一个类,而这个类的名字又和脚本文件的名字一致,这种情况下就和Java是一样的,即生成与所定义的类一致的class文件, Groovy类都会实现groovy.lang.GroovyObject接口。
对于多个类
如果Groovy脚本文件含有一个或多个类,groovy编译器会很乐意地为每个类生成一个对应的class文件。如果想直接执行这个脚本,则脚本里的第一个类必须有一个static的main方法。
对于有定义类的脚本
如果Groovy脚本文件有执行代码, 并且有定义类, 那么所定义的类会生成对应的class文件, 同时, 脚本本身也会被编译成一个Script的子类,类名和脚本文件的文件名一样
1. public static void main vs script
Groovy 同时支持脚本和类。以以下代码为例:
Main.groovy
class Main {
static void main(String... args) {
println 'Groovy world!'
}
}
定义类,名称是Main
main方法用作类的主要方法 public static void main(String[])
方法的主体println 'Groovy world!'
这是典型的java代码,其中代码必须嵌入到一个类中方可执行。Groovy 使它更简洁:
Main.groovy
println 'Groovy world!'
脚本可以被视为类,无需声明它,但存在一些差异。
2. 脚本转换为类
脚本始终会被编译为类。Groovy 编译器将为你编译该类,并将脚本的正文复制到方法中。因此,前面的示例的编译情况类似如下:
Main.groovy
import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script {
def run() {
println 'Groovy world!'
}
static void main(String[] args) {
InvokerHelper.runScript(Main, args)
}
}
类继承自 groovy.lang.Script
groovy.lang.Script
需要一个带返回值的方法run
脚本正文进入方法run
该方法是自动生成的main
并委托对方法执行脚本run
如果脚本位于文件中,则该文件的基名用于确定生成的脚本类的名称
3.方法
可以定义方法到脚本中,如下所示
int fib(int n) {
n < 2 ? 1 : fib(n-1) + fib(n-2)
}
assert fib(10)==89s
您还可以混合方法和代码。生成的脚本类将所有方法都引入脚本类,并将所有脚本体组合到方法中:
//脚本开始
println 'Hello'
//在脚本正文中定义方法
int power(int n) { 2**n }
//脚本继续
println "2^6==${power(6)}"
此代码在内部转换为:
import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script {
int power(int n) { 2** n}
def run() {
println 'Hello'
println "2^6==${power(6)}"
}
static void main(String[] args) {
InvokerHelper.runScript(Main, args)
}
}
4.变量
脚本中的变量不需要类型定义。这意味着此脚本:
int x = 1
int y = 2
assert x+y == 3
行为与以下行为相同:
x = 1
y = 2
assert x+y == 3
但是,两者之间存在语义差异:
如果变量声明为第一个示例中,则它是局部变量。它将在编译器生成的方法run
中声明,并且在脚本主体之外不可见。特别是此变量在脚本的其他方法中不可见。
如果变量未声明类型或def占位符,则进入脚本绑定。绑定从方法中可见,如果使用脚本与应用程序交互,并且需要在脚本和应用程序之间共享数据,则绑定尤其重要。
如果希望变量成为类的字段而不进入Binding
,可以使用@Field注释。