Groovy与Java的比较(上)
1.支持函数式编程,不需要main函数
2.默认导入常用的包,包括:
java.io
java.math
java.net
java.util
groovy.lang
groovy.util
3.断言不支持jvm的-ea参数进行开关
4.支持对对象进行布尔求值

5.类不支持default作用域,且默认作用域为public
6.受检查类型异常(Checked Exception)也可以不用捕获
7.一些新的运算符

8.groovy中基本类型也是对象,可以直接调用对象的方法,如:
?
- assert (-12345).abs() == 12345
但浮点运算是基于BigDecimal类
?
- assert 0.25 instanceof BigDecimal
-
assert 0.1 * 3 == 0.3
-
assert 1.1 + 0.1 == 1.2
-
assert 1 / 0.25 == 4
Groovy与Java的比较(中)
9.字符串的处理
String对象和java类似,但没有character的概念,没有迭代每个字符的方法。
使用单引号定义普通字符串,双引号定义的字符串可以包含Groovy运算符,$符号则需要转义("$"),如:
?
- String name = "Ben"
-
String greeting = "Good morning,${name}"
-
assert greeting == 'Good morning,Ben'
-
String output = "The result of 2 + 2 is: ${2 + 2}"
-
assert output == "The result of 2 + 2 is: 4"
还可以使用三个连续的"来定义多行字符串,如:
?
- String getEmailBody(String name) {
-
return """Dear ${name},
-
Thank you for your recent inquiry. One of our team members
- will process it shortly and get back to you. Some time in
- the next decade. Probably.
- Warmest and best regards,
- Customer Services
-
"""
-
- }
char类型的使用方法:
?
- char ch = 'D'
-
assert ch instanceof Character
-
String str = "Good morning Ben"
-
str = str.replace(' ' as char,'+' as char)
-
assert str == "Good+morning+Ben"
10.as运算符,用于没有集成关系的类型间强制类型转换,如:
?
- assert 543667 as String == "543667"
-
assert 1234.compareTo("34749397" as int) < 0
可通过实现asType(Class) 方法来实现自定义的as行为,默认的方法包括:

11.一些集合类型的语法甜头(Syntax sugar for lists,maps,and ranges)
从语言层面支持ListMapRange类型,而不是通过SDK中的类
使用[]创建创建和初始化List、Map,如:
?
- List myList = [ "apple","orange","lemon" ]
-
Map myMap = [ 3: "three",6: "six",2: "two" ]
-
assert 3 == [ 5,6,7 ].size()
ListMap支持数组风格的用法
?
- List numbers = [ 5,10,15,20,25 ]
-
assert numbers[0] == 5
-
assert numbers[3] == 20
-
assert numbers[-1] == 25
-
assert numbers[-3] == 15
-
numbers[2] = 3
-
assert numbers[2] == 3
-
numbers < < 30
-
assert numbers[5] == 30
-
-
Map items = [ "one": "apple",
-
"two": "orange",
-
"three": "pear",92); word-break:normal; border-top:medium none; border-right:medium none; padding-top:0px!important">
"four": "cherry" ]
-
assert items["two"] == "orange"
-
assert items["four"] == "cherry"
-
items["one"] = "banana"
-
assert items["one"] == "banana"
-
items["five"] = "grape"
-
assert items["five"] == "grape"
新的类型:Range
Range实现了java.util.List,可以作为List使用,并扩展了包含(..)和排除(..< )运算符
?
-
-
-
def range = 5..8
-
assert range.size() == 4
-
assert range.get(2) == 7
-
assert range[2] == 7
-
assert range instanceof java.util.List
-
assert range.contains(5)
-
assert range.contains(8)
-
-
-
range = 5..< 8
-
assert range.size() == 3
-
assert range.get(2) == 7
-
assert range[2] == 7
-
assert range instanceof java.util.List
-
assert range.contains(5)
-
assert ! range.contains(8)
-
-
-
def range = 1..10
-
assert range.from == 1
-
assert range.to == 10
-
-
- List fruit = [
-
-
"apple",
-
"pear",92); word-break:normal; border-top:medium none; border-right:medium none; padding-top:0px!important">
"lemon",
-
"orange",92); word-break:normal; border-top:medium none; border-right:medium none; padding-top:0px!important">
"cherry" ]
-
for (int i in 0..< fruit.size()) {
-
println "Fruit number $i is '${fruit[i]}'"
- }
-
List subList = fruit[1..3]
12.一些省时的特性
行末的分号(;)不是必须的。在没有分号的情况下,groovy计算一行如果是有效的表达式,则认为下一行是新的表达式,否则将联合下一行共同作为一个表达式。分隔多行的表达式,可以用/符号,如:
?
- String fruit = "orange,apple,pear,"
-
+ "banana,cherry,nectarine"
方法调用时的圆括号()不是必须的(但建议保留)。但在无参方法调用,或第一个参数是集合类型定义时还是必须的:
?
- println "Hello,world!"
- println()
-
println([1,2,3,4])
方法定义中的return语句不是必须的,没有return的情况下,将返回方法体中最后一行的值,如下面的方法返回value+1:
int addOne(int value) { value + 1 }
Groovy与Java的比较(下)
13.语言级别的正则表达式支持
使用斜线(/)定义正则表达式,避免java中的多次转义,如"\w"相当于/w/。
如果要作为java中的Pattern对象使用,可以使用~符号表示,如:
?
- assert ~"London" instanceof java.util.regex.Pattern
-
assert ~/w+/ instanceof java.util.regex.Pattern
使用=~运算符进行匹配
?
- assert "Speaking plain English" =~ /plain/
使用==~运算符进行精确匹配
?
- assert !("Speaking plain English" ==~ /plain/)
-
assert "Speaking plain English" ==~ /.*plain.*/
捕获分组,如:
?
- import java.util.regex.Matcher
-
String str = "The rain in Spain falls mainly on the plain"
- Matcher m = str =~ /b(w*)ain(w*)b/
-
if (m) {
-
for (int i in 0..< m.count) {
-
println "Found: '${m[i][0]}' - " +
-
"prefix: '${m[i][1]}'" +
-
",suffix: '${m[i][2]}'"
- }
- }
输出:
?
- Found: 'rain' - prefix: 'r',suffix: ''
-
Found: 'Spain' - prefix: 'Sp',suffix: ''
-
Found: 'mainly' - prefix: 'm',suffix: 'ly'
-
Found: 'plain' - prefix: 'pl',suffix: ''
14.简化的javabean
直接使用“.属性名”的方法代替getter,如:
?
- Date now = new Date()
-
println "Current time in milliseconds: ${ now.time }"
- now.time = 103467843L
-
assert now.time == 103467843L
属性定义不需要setter/getter。未指定作用域的属性,groovy自动认为是private并生为其成setter/getter,也可以根据需要进行覆写。如下除了最后一个字段,都是属性:
?
- class MyProperties {
-
static String classVar
-
final String constant = "constant"
- String name
-
public String publicField
-
private String privateField
- }
简化bean的初始化,可以使用Map进行初始化,或键值对的方法,如
?
- DateFormat format = new SimpleDateFormat(
-
lenient: false,
- numberFormat: NumberFormat.getIntegerInstance(),
-
timeZone: TimeZone.getTimeZone("EST"))
可以使用属性的方式读取map:
?
- Map values = [ fred: 1,peter: 5,glen: 42 ]
-
assert values.fred == 1
-
values.peter = 10
-
assert values.peter == 10
注:groovy将map的key作为字符串处理,除非是数字或者用圆括号包含。这里的fred就是字符串"fred",但引号不是必须的,只有在key包含空格、句点或其他不能作为Groovy标示符的字符存在时才需要。如果需要使用一个变量的值作为key,则使用圆括号,如 [ (fred): 1 ]。
15.groovy不具备的java特性
不能用单引号定义字符类型,但可以使用as运算符将一个字母的字符串转换为字符类型
for循环中不能用逗号分隔多个运算符,如下面的代码是不允许的:
?
- for (int i = 0,j = 0; i < 10; i++,j++) { ... }
不支持DO...WHILE循环,但可以使用while...for运算代替
不支持内部类和匿名类,但支持闭包和在一个文件中定义多个类
16.groovy的重要特性——闭包:
可以看作一个匿名方法定义,可以赋予给一个变量名、作为参数传递给方法调用、或者被方法返回。也可以想象为只有一个方法定义的匿名类。
闭包的语法{ < arguments> -> < body> },如:
?
- List fruit = [ "apple","Orange","Avocado","pear","cherry" ]
- fruit.sort { String a,String b -> a.compareToIgnoreCase(b) }
-
println "Sorted fruit: ${fruit}"
注:sort方法只有一个闭包类型的参数,省略了圆括号;闭包中使用了默认的return值
当没有参数传入时,仍然需要保留箭头的存在{-> ... }
只有一个参数传入时,可以省略箭头,隐式的创建一个it参数,引用当前对象,如:
?
- [ "apple","cherry" ].each { println it }
可以将闭包赋予一个变量,如
?
- Closure comparator = { String a,String b ->
- a.compareToIgnoreCase(b)
- }
-
List fruit = [ "apple","cherry" ]
- fruit.sort(comparator)
-
println "Sorted fruit: ${fruit}"
-
assert comparator("banana","Lemon") < 0
只有一个参数的闭包,可以不传入参数,运行时隐式的传入null参数
当闭包是一个方法的最后一个参数时,可以写在圆括号外面,如:
?
- List list = [ 1,5,6 ]
-
list.inject(0,{ runningTotal,value -> runningTotal + value })
可以这样写:
?
- assert 15 == list.inject(0) { runningTotal,value -> runningTotal + value }
便于闭包中具有多行时代码更加清晰
不要滥用闭包。当闭包作为一个属性时,不要在子类中覆写,实在需要这样做,使用方法。使用闭包也无法利用java中很多AOP框架的特性
17.groovy的重要特性——动态编程
动态的使用属性,如下的java代码:
?
- public void sortPeopleByGivenName(List< Person> personList) {
-
Collections.sort(personList,new Comparator< Person>() {
-
public int compare(Person p1,Person p2) {
-
return p1.getFamilyName().compareTo(p2.getFamilyName());
- }
- } ) ;
- }
可使用下面的代替,当需要使用其他字段比较时,不需要修改代码
?
- def sortPeople(people,property) {
-
people.sort { p1,p2 -> p1."${property}" < => p2."${property}" }
- }
将一个String作为属性或方法名进行调用,如:
?
- peopleList.sort()
-
peopleList."sort"()
动态类型(duck typing:"if it walks like a duck and talks like a duck,it’s probably a duck):运行期解析对象的属性和方法,允许在运行时增加对象的属性和方法而不修改源代码,因此可能出现调用未定义方法的情况。
动态编程带来的危险:
编译器不能检查到类型错误、方法或属性的错误调用,应该养成编写测试的习惯
难以调试,使用“单步跳入(step into)”经常进入一些反射中,使用“运行到光标处(run to cursor)”代替
动态的类型定义使代码难以阅读,使用良好的命名、注释,尽量明确定义变量类型,便于IDE检测ht potential type errors in the call潜在的错误。
18.Groovy JDK中的增强
Collection/Array/String具有size()方法
Collection/Array/String具有each(closure)方法,方便的进行遍历
Collection/Array/String具有find(closure)、findAll(closure)方法,find返回第一个符合条件的对象,findAll返回所有符合条件对象列表,如:
?
- def glen = personList.find { it.firstName == "Glen" }
Collection/Array/String具有collect(closure)方法,对集合中每个对象执行一段方法后,返回结果集,如:
?
- def names = [ "Glen","Peter","Alice","Graham","Fiona" ]
-
assert [ 4,5 ] == names.collect { it.size() }
Collection/Array/String具有sort(closure)方法,包括:
一个参数的闭包,如:
?
"Ann","Veronica" ]
- def sortedNames = names.sort { it.size() }
-
assert [ "Ann","Glen","Veronica" ] == sortedNames
两个参数的闭包,如:
?
def sortedNames = names.sort { name1,name2 ->
- name1.size() < => name2.size()
- }
-
assert [ "Ann","Veronica" ] == sortedNames
Collection/Array具有join(String)方法
?
assert "Glen,Peter,Alice,Fiona" == names.join(",")
File.text属性读取文件内容作为字符串返回
File.size()方法返回文件的byte值,相当于File.length()方法
File.withWriter(closure)方法,从文件创建一个Writer对象传给闭包,闭包执行完毕后,依赖的输出流自动安全关闭。另外还有若干with...方法查看文档
Matcher.count返回相应Matcher的匹配数量
Number.abs()方法,对数字求绝对值
Number.times(closure)执行n次闭包,将当前执行的次数作为参数传给闭包
19.XML的处理
示例的XML:
?
- < root>
-
< item qty="10">
-
< name>Orange< /name>
-
< type>Fruit< /type>
-
< /item>
-
< item qty="6">
-
< name>Apple< /name>
-
< item qty="2">
-
< name>Chair< /name>
-
< type>Furniture< /type>
-
< /item>
-
< /root>
处理程序
?
- import groovy.xml.MarkupBuilder
-
import groovy.util.XmlSlurper
-
def file = new File("test.xml")
- def objs = [
-
[ quantity: 10,name: "Orange",type: "Fruit" ],92); word-break:normal; border-top:medium none; border-right:medium none; padding-top:0px!important">
[ quantity: 6,name: "Apple",
-
[ quantity: 2,name: "Chair",type: "Furniture" ] ]
-
-
def b = new MarkupBuilder(new FileWriter(file)) 创建MarkupBuilder对象
- b.root {
- 动态调用root方法,但builder对象并没有该方法,把它作为一个新的XML对象的根节点,并且把方法名作为根节点名称
-
- objs.each { o ->
- item(qty: o.quantity) {
- name(o.name)
- type(o.type)
- }
- }
- }
- 遍历集合,创建节点,其中item/name/type也是动态的方法,以方法名作为节点名,方法参数作为节点的属性
-
-
def xml = new XmlSlurper().parse(file)
- 使用XmlSlurper对象解析内存中的XML文件
-
assert xml.item.size() == 3
-
assert xml.item[0].name == "Orange"
-
assert xml.item[0].@qty == "10"
- 使用动态的属性名读取XML节点
- 使用@字符读取节点属性
-
-
println "Fruits: ${xml.item.findAll {it.type == 'Fruit'}*.name }"
-
println "Total: ${xml.item.@qty.list().sum {it.toInteger()} }"
20.最佳实践
使用地道的Groovy语法:尽可能使用groovy中简化后的语法风格,减少代码量
实验:使用groovy console或shell可以方便的实验groovy代码
尽可能使用方法,而不是闭包。方法易于理解,也利于和java交互
在方法签名中尽可能的使用确定的类型,便于代码阅读和IDE的错误检测。在使用动态类型时要有清晰完善的文档注释