什么是 Groovy?
简言之,Groovy是一种基于JVM(Java虚拟机)的敏捷动态开发语言。它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言。作为Java程序员,即便以前没有接触过Groovy,也可以快速学习。
Groovy 和 Java 语言对比
用 Java 编写的典型的 Hello World 示例如下所示:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("java:Hello World");
}
}
用 Groovy 编写的 Hello World
Groovy 支持松散的 Java 语法 — 例如,不需要为打印 “Hello World!” 这样的简单操作定义类。
而且,Groovy 使日常的编码活动变得更容易,例如,Groovy 允许输入println,而无需输入System.out.println。当您输入println时,Groovy 会非常聪明地知道您指的是System.out。
所以,用 Groovy 编写 Hello World 程序就如下面这样简单:
println "Hello World!"
事实证明,使用Groovy可以达到事半功倍的效果!
本教程采用的是在eclipse中安装Groovy插件,(具体安装方法见百度),通过建立Groovy项目,和Java实例进行比较来学习。
基础
Groovy注释标记和Java一样,支持//或者//
Groovy语句可以不用分号结尾。
Groovy中支持动态类型,即定义变量的时候可以不指定其类型。Groovy中,变量定义可以使用关键字def。
def var=1
def str="iamaperson"
def intx=1 //也可以指定类型
函数定义时,参数的类型也可以不指定。比如
Stringfunction(arg1,args2){//无需指定参数类型}
除了变量定义可以不指定类型外,Groovy中函数的返回值也可以是无类型的。比如:
//无类型的函数定义,必须使用def关键字
def nonReturnTypeFunc(){
last_line//最后一行代码的执行结果就是本函数的返回值
}
//如果指定了函数返回类型,则可不必加def关键字来定义函数
String getString(){
return "I am a string"
}
Groovy是基于Java的,而且最终会转成Java Code运行在JVM上
Groovy对字符串支持相当强大,充分吸收了一些脚本语言的优点:
1 单引号''中的内容严格对应Java中的String,不对$符号进行转义
def singleQuote='Iam$dolloar' //输出就是Iam$dolloar
2 双引号""的内容则和脚本语言的处理有点像,如果字符中有$号的话,则它会$表达式先求值。
def doubleQuoteWithoutDollar="I am one dollar" //输出 I am one dollar
def x=1
def doubleQuoteWithDollar="I am $x dolloar" //输出I am 1 dolloar
3 三个引号'''xxx'''中的字符串支持随意换行 比如
def multieLines =''' begin
line 1
line 2
end '''
最后,除了每行代码不用加分号外,Groovy中函数调用的时候还可以不加括号。比如:
println("test")---> println "test"
注意,虽然写代码的时候,对于函数调用可以不带括号,但是Groovy经常把属性和函数调用混淆。比如
def getSomething(){"hello"}
get Something() //如果不加括号的话,Groovy会误认为getSomething是一个变量。
所以,调用函数要不要带括号,个人觉得如果这个函数是Groovy API或者Gradle API中比较常用的,比如println,就可以不带括号。否则还是带括号。
Groovy类:
1,不需要public修饰
2,不需要类型说明
3,不需要get/set方法
4,不需要构造函数
5,不需要return——默认返回方法中的最后一行
6,方法调用时可以省略() //构造函数除外
Groovy 是没有类型的 Java 代码
在 Java 中,如果要声明一个String变量,则必须输入:
String value = "Hello World";
但是,如果仔细想想,就会看出,等号右侧的字符已经表明value的类型是String。所以,Groovy 允许省略value前面的String类型变量,并用def代替。
def value = "Hello World"
实际上,Groovy 会根据对象的值来判断它的类型。
将 HelloWorld.groovy 文件中的代码编辑成下面这样:
String message = "Hello World"
println message
运行这段代码,应该会在控制台上看到与前面一样的 “Hello World”。现在,将变量类型String替换为def并重新运行代码。是不是注意到了相同的结果?
除了输出message的值,还可以用以下调用输出它的类型:
println message.class
输出 “class java.lang.String” Groovy 推断出message一定是String类型的,因为它的值是用双引号括起来的。
再来看如下赋值:
def message = 12
println message.class
message变量的数字值看起来像是 Java 的原生类型int。但是,运行这个代码就可以看出,Groovy 将它作为Integer。这是因为在 Groovy 中 “一切都是对象”。
Java 中的所有对象都扩展自java.lang.Object,这对 Groovy 来说非常方便。即使在最糟的情况下,Groovy 运行时不能确定变量的类型,它只需将变量当成Object,问题就解决了。
继续使用这段代码。将message改成自己喜欢的任意类型:Groovy 会在运行时尽其所能推断出这个变量的类型。
无类型有什么意义?
Groovy 缺少类型意味着所需的输入更少。更重要的是,这意味着要阅读的代码要少得多。最后,Groovy 缺少类型能够带来更高的灵活性 — 不需要接口或抽象类。
所以,只需要使用def关键字就能在方法中声明一个独立变量,不需要将def关键字作为方法声明中的参数。在for循环声明中也不需要它,这意味着不用编写(int x = 0; x < 5; x++),相反,可以省略int,保留空白。
通过 Groovy 进行循环
同大多数脚本语言一样,Groovy 经常被宣传为生产力更高的 Java 语言替代品。
首先,用与创建HelloWorld相同的方式创建一个 Groovy 类,并删除自动生成的类体:将要定义一个独立的repeat函数。现在在控制台中输入以下代码:
def repeat(val){
for(i = 0; i < 5; i++){
println val
}
}
起初,从 Java 的角度来看,这个小函数看起来可能有些怪(实际上,它很像 JavaScript)。但它就是 Java 代码,只不过是用 Groovy 的样式编写的。
repeat函数接受一个变量val。请注意参数不需要def。方法体本质上就是一个for循环。
调用这个函数。
repeat("hello world")
会输出 “hello world” 五次。请注意,for循环中省略了int。没有变量类型的for循环要比标准的 Java 代码短些。现在看看如果在代码里加入范围会出现什么情况。
Groovy 中的范围
范围是一系列的值。例如 “0..4” 表明包含整数 0、1、2、3、4。Groovy 还支持排除范围,“0..<4” 表示 0、1、2、3。还可以创建字符范围:“a..e” 相当于 a、b、c、d、e。“a..e的所有值。
循环范围
范围为循环带来了很大的方便。例如,前面从 0 递增到 4 的for循环如下所示:
for(i = 0; i < 5; i++)
范围可以将这个for循环变得更简洁,更易阅读:
def repeat(val){
for(i in 0..5){
println val
}
}
设置范围
如果运行这个示例,可能会注意到一个小问题:“Hello World” 输出了六次而不是五次。这个问题有三种解决方法:
将包含的范围限制到 4: for(i in 0..4)
从 1 而不是 0 开始: for(i in 1..5)
将范围由包含改为排除:for(i in 0..<5)
不论采用哪种方法,都会得到原来的效果 — 输出 “Hello World” 五次。
默认参数值
现在已经成功地使用 Groovy 的范围表达式缩短了repeat函数。但这个函数依然有些限制。如果想重复 “Hello World” 八次该怎么办?如果想对不同的值重复不同次数 — 比如 “Hello World” 重复八次,“Goodbye Sunshine” 重复两次,这时该怎么办?
每次调用repeat时都要指定需要的重复次数的做法已经过时了,特别是在已经适应了默认行为(重复五次)的时候。
Groovy 支持默认参数值,可以在函数或方法的正式定义中指定参数的默认值。调用函数的程序可以选择省略参数,使用默认值。
def repeat(val, repeat=5){
for(i in 0..repeat){
println val
}
}
像下面这样调用该函数:
repeat("Hello World", 2)
repeat("Goodbye sunshine", 4)
repeat("foo")
结果会输出 “Hello World” 两次,“Goodbye sunshine” 四次,“foo” 五次(默认次数)。
Groovy 集合
在 Groovy 提供的所有方便的快捷方式和功能中,最有帮助的一个可能就是内置的集合。在 Java 编程中使用集合— 导入java.util类,初始化集合,将项加入集合。这三个步骤都会增加不少代码。
而 Groovy 可以直接在语言内使用集合。在 Groovy 中,不需要导入专门的类,也不需要初始化对象。集合是语言本身的本地成员。Groovy 也使集合(或者列表)的操作变得非常容易,为增加和删除项提供了直观的帮助。
def range = 0..4 //把范围当集合
println range.class
assert range instanceof List //证明了range是集合
Groovy 的集合支持相当丰富,而且美妙之处就在于,在 Groovy 的魔法背后,一切都是标准的 Java 对象。每个 Groovy 集合都是java.util.Collection或java.util.Map的实例。
def coll = ["Groovy", "Java", "Ruby"]
assert coll instanceof Collection
assert coll instanceof ArrayList
你将会注意到,coll对象看起来很像 Java 语言中的数组。实际上,它是一个Collection。要在普通的 Java 代码中得到集合的相同实例,必须执行以下操作:
Collection coll = new ArrayList();
coll.add("Groovy");
coll.add("Java");
coll.add("Ruby");
在 Java 代码中,必须使用add()方法向ArrayList实例添加项。
Groovy 提供了许多方法可以将项添加到列表 — 可以使用add()方法(因为底层的集合是一个普通的ArrayList类型),但是还有许多快捷方式可以使用。
例如:
coll.add("Python")
coll << "Smalltalk"
coll[5] = "Perl"
请注意,Groovy 支持操作符重载 —<<操作符被重载,以支持向集合添加项。还可以通过位置参数直接添加项。在这个示例中,由于集合中只有四个项,所以[5]操作符将 “Perl” 放在最后。请自行输出这个集合并查看效果。
Groovy 还允许在集合中增加或去掉集合,如下所示:
def numbers = [1,2,3,4]
assert numbers + 5 == [1,2,3,4,5]
assert numbers - [2,3] == [1,4]
在上面的代码中, 实际上创建了新的集合实例,由最后一行可以看出。
魔法方法
Groovy 还为集合添加了其他一些方便的功能。例如,可以在集合实例上调用特殊的方法,如下所示:
def numbers = [1,2,3,4]
assert numbers.join(",") == "1,2,3,4"
assert [1,2,3,4,3].count(3) == 2
join()和count()只是在任何项列表上都可以调用的众多方便方法中的两个。分布操作符(spread operator)是个特别方便的工具,使用这个工具不用在集合上迭代,就能够调用集合的每个项上的方法。
假设有一个String列表,现在想将列表中的项目全部变成大写,可以编写以下代码:
assert ["JAVA", "GROOVY"] ==["Java", "Groovy"]*.toUpperCase()
请注意*.标记。对于以上列表中的每个值,都会调用toUpperCase(),生成的集合中每个String实例都是大写的。
Groovy 映射
除了丰富的列表处理功能,Groovy 还提供了坚固的映射机制。同列表一样,映射也是本地数据结构。而且 Groovy 中的任何映射机制在幕后都是java.util.Map的实例。
Java 语言中的映射是名称-值对的集合。所以,要用 Java 代码创建典型的映射,必须像下面这样操作:
Map map = new HashMap();
map.put("name", "Andy");
map.put("VPN-#","45");
Groovy 使得处理映射的操作像处理列表一样简单 — 例如,可以用 Groovy 将上面的 Java 映射写成
def hash = [name:"Andy", "VPN-#":45]
请注意,Groovy 映射中的键不必是String。在这个示例中,name看起来像一个变量,但是在幕后,Groovy 会将它变成String。
全都是 Java
接下来创建一个新类Mapper并加入上面的代码。然后添加以下代码,以证实底层的代码是真正的 Java 代码:
assert hash.getClass() == java.util.LinkedHashMap
可以看到 Groovy 使用了 Java 的LinkedHashMap类型,这意味着可以使用标准的 Java 一样语句对hash中的项执行put和get操作。
hash.put("id", 23)
assert hash.get("name") == "Andy"
有 groovy 特色的映射
hash.dob = "01/29/76"
.符号还可以用来获取项。例如,使用以下方法可以获取dob的值:
assert hash.dob == "01/29/76"
Groovy 中的闭包
不再需要更多迭代
虽然在前几节编写了不少集合代码,但还没有实际地在集合上迭代。当然,您知道 Groovy 就是 Java,所以如果愿意,那么总是能够得到 Java 的Iterator实例,用它在集合上迭代,就像下面这样:
def acoll = ["Groovy", "Java", "Ruby"]
for(Iterator iter = acoll.iterator(); iter.hasNext();){
println iter.next()
}
实际上在for循环中并不需要类型声明,因为 Groovy 已经将迭代转变为任何集合的直接成员。在这个示例中,不必获取Iterator实例并直接操纵它,可以直接在集合上迭代。而且,通常放在循环构造内的行为(例如for循环体中println)接下来要放在闭包内。在深入之前,先看看如何执行这步操作。
能否看见闭包?
对于上面的代码,可以用更简洁的方式对集合进行迭代,如下所示:
def acoll = ["Groovy", "Java", "Ruby"]
acoll.each{
println it
}
请注意,each直接在acoll实例内调用,而acoll实例的类型是ArrayList。在each调用之后,引入了一种新的语法 —{,然后是一些代码,然后是}。由{}包围起来的代码块就是闭包。
执行代码:
闭包中的it变量是一个关键字,指向被调用的外部集合的每个值 — 它是默认值,可以用传递给闭包的参数覆盖它。下面的代码执行同样的操作,但使用自己的项变量:
def acoll = ["Groovy", "Java", "Ruby"]
acoll.each{ value ->
println value
}
在这个示例中,用value代替了 Groovy 的默认it。
迭代无处不在
闭包在 Groovy 中频繁出现,但是,通常用于在一系列值上迭代的时候。请记住,一系列值可以用多种方式表示,不仅可以用列表表示 — 例如,可以在映射、String、JDBCRowset、File的行上迭代,等等。
对于前面的例子,可以编写以下代码:
def hash = [name:"Andy", "VPN-#":45]
hash.each{ key, value ->
println "${key} : ${value}"
}
请注意,闭包还允许使用多个参数 — 在这个示例中,上面的代码包含两个参数(key和value)。
使用 Java 代码迭代
Mapmap = new HashMap();
map.put("name", "Andy");
map.put("VPN-#","45");
for(Iterator iter = map.entrySet().iterator(); iter.hasNext();){
Map.Entry entry = (Map.Entry)iter.next();
System.out.println(entry.getKey() + " : " + entry.getValue());
}
很明显,上面的代码比 Groovy 的代码长得多,如果要处理大量集合,那么显然用 Groovy 处理会更方便。
闭包的更多使用方式
虽然在迭代上使用闭包的机会最多,但闭包确实还有其他用途。因为闭包是一个代码块,所以能够作为参数进行传递(Groovy 中的函数或方法不能这样做)。闭包在调用的时候才会执行这一事实(不是在定义的时候)使得它们在某些场合上特别有用。
例如,通过 Eclipse 创建一个ClosureExample对象,并保持它提供的默认类语法。在生成的main()方法中,添加以下代码:
def excite = { word ->
return "${word}!!"
}
这段代码是名为excite的闭包。这个闭包接受一个参数(名为word),返回的String是word变量加两个感叹号。请注意在String实例中替换的用法。在String中使用${value}语法将告诉 Groovy 替换String中的某个变量的值。可以将这个语法当成return word + "!!"的快捷方式。
延迟执行
assert "Groovy!!" == excite("Groovy")
ssert "Java!!" == excite.call("Java")
可以看到,两种调用方式都能工作,但是直接调用的方法更简洁。
文件I/O操作
直接来看例子吧,虽然比Java看起来简单,但要理解起来其实比较难。尤其是当你要自己查SDK并编写代码的时候。
整体说来,Groovy的I/O操作是在原有Java I/O操作上进行了更为简单方便的封装,并且使用Closure来简化代码编写。主要封装了如下一些了类:
读文件
Groovy中,文件读操作简单到令人发指:
def targetFile = new File(文件名) <==File对象还是要创建的。
读该文件中的每一行:eachLine的唯一参数是一个Closure。Closure的参数是文件每一行的内容
写文件:
结束语
通过本篇的学习,进一步认识到 Groovy 就是 Java,只是缺少了过去使用Java的许多语法规则。Groovy 是没有类型、没有修改符、没有 return、没有Iterator、不需要导入集合的 Java。简而言之,Groovy 就是丢掉了许多包袱的 Java,这些包袱可能会压垮 Java 项目。
但是在幕后,Groovy 就是 Java。