groovy学习之路

什么是 Groovy?


简言之,Groovy是一种基于JVMJava虚拟机)的敏捷动态开发语言。它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言。作为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。

推荐阅读更多精彩内容