转载:grails学习笔记——Groovy与java的比较

grails学习笔记——Groovy与java的比较

1.支持函数式编程,不须要main函数
2.默认导入经常使用的包,包括:
  • java.io  
  • java.math  
  • java.net  
  • java.util  
  • groovy.lang  
  • groovy.util 
3.断言不支持jvm的-ea参数进行开关
4.支持对对象进行布尔求值
Type Evaluates to false
Boolean   false, Boolean.FALSE
Object reference null
Number  0
String, GString Zero-length string
Collection Empty Collection
Map Empty Map
Iterator hasNext() returns false
Enumeration hasMoreElements() returns false
java.util.regex.Matcher find() returns false
5.类不支持default做用域,且默认做用域为public
6.受检查类型异常(Checked Exception)也能够不用捕获
7.一些新的运算符
运算符 用法 至关于JAVA
?.        f (obj?.value != null) {
    . . .
}
if (obj != null && obj.value != null) {
    . . .
}
?:
String name = person.getName() ?: "<unknown>" String name = (person.getName() != null) ? person.getName() : "<unknown>"
*.
List names = people*.name List names = new ArrayList();
for(Iterator it = people.iterator();it.hasNext();){
    People people = (People)it.next();
    names.add(people.getName());
}
<=> public int compare(int i1, int i2) {
    return i1 <=> i2;
}
public int compare(int i1, int i2) {
    if (i1 == i2) return 0;
    else if (i1 < i2) return -1;
    else return 1;
}
== String str = null
assert str == null
assert str != "test"

str = "test"
assert str != null
assert str == "test"
str += " string"
assert str == "test string"
至关与equals方法,若是左侧的对象实现了compareTo方法,则调用compareTo方法且返回0时,返回true.而对象的比较则要调用is方法,如:
BigDecimal x = 0.234
BigDecimal y = x
assert y.is(x)
assert !y.is(0.234)
assert y == 0.234
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
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行为,默认的方法包括:
源类型 目标类型 说明
String Number (int, double, Long,BigDecimal, and so on) 转换成相应的数字类型,若是字符串不是数字,则抛出NumberFormatException
List Array 将List转换成Array,如myList as String[]
List 或 array Set 将List或array直接转换为setb
任意对象
boolean 使用groovy的布尔运算逻辑进行求值
Collection List 复制原始collection中的对象建立一个List,对象在List中的顺序由原collection的迭代顺序决定
String List 将字符串转换为每一个字符的序列,并放到List中
11.一些集合类型的语法甜头(Syntax sugar for lists, maps, and ranges)
  • 从语言层面支持List\Map\Range类型,而不是经过SDK中的类
  • 使用[]建立建立和初始化List、Map,如:
List myList = [ "apple", "orange", "lemon" ]
Map myMap = [ 3: "three", 6: "six", 2: "two" ]
assert 3 == [ 5, 6, 7 ].size()
  • List\Map支持数组风格的用法
List numbers = [ 5, 10, 15, 20, 25 ]
assert numbers[0] == 5        //获取List中的对象
assert numbers[3] == 20
assert numbers[-1] == 25    //逆序获取List对象
assert numbers[-3] == 15
numbers[2] = 3                //更新List对象
assert numbers[2] == 3
numbers << 30                 //添加数据
assert numbers[5] == 30

Map items = [ "one":   "apple",
              "two":   "orange",
              "three": "pear",
              "four":  "cherry" ]
assert items["two"] == "orange" //从Map中得到对象
assert items["four"] == "cherry"
items["one"] = "banana"             //更新Map中对象
assert items["one"] == "banana"
items["five"] = "grape"             //增长对象到中
assert items["five"] == "grape"
  • 新的类型:Range
Range实现了java.util.List,能够做为List使用,并扩展了包含(..)和排除(..<)运算符
        // an inclusive range
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)

// lets use an exclusive range
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)

//get the end points of the range without using indexes
def range = 1..10
assert range.from == 1
assert range.to == 10

        List fruit = [
    "apple",
    "pear",
    "lemon",
    "orange",
    "cherry" ]
for (int i in 0..<fruit.size()) {                     //Iterates through an exclusive range B
    println "Fruit number $i is '${fruit[i]}'"
}
List subList = fruit[1..3]               //Extracts a list slice C
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 }
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", "pear", "cherry" ].each { println it }
  • 能够将闭包赋予一个变量,如
    Closure comparator = { String a, String b ->
        a.compareToIgnoreCase(b)
    }
    List fruit = [ "apple", "Orange", "Avocado", "pear", "cherry" ]
    fruit.sort(comparator)
    println "Sorted fruit: ${fruit}"
    assert comparator("banana", "Lemon") < 0 
  • 只有一个参数的闭包,能够不传入参数,运行时隐式的传入null参数
  • 当闭包是一个方法的最后一个参数时,能够写在圆括号外面,如:
    List list = [ 1, 3, 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, 5, 6, 5 ] == names.collect { it.size() }
  • Collection/Array/String具备sort(closure)方法,包括:
    一个参数的闭包,如:
    def names = [ "Glen", "Peter", "Ann", "Graham", "Veronica" ]
    def sortedNames = names.sort { it.size() }
    assert [ "Ann", "Glen", "Peter", "Graham", "Veronica" ] == sortedNames
    两个参数的闭包,如:
    def names = [ "Glen", "Peter", "Ann", "Graham", "Veronica" ]
    def sortedNames = names.sort { name1, name2 ->
        name1.size() <=> name2.size()
    }
    assert [ "Ann", "Glen", "Peter", "Graham", "Veronica" ] == sortedNames
  • Collection/Array具备join(String)方法
    def names = [ "Glen", "Peter", "Alice", "Fiona" ]
    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>
        <type>Fruit</type>
      </item>
      <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" ],
        [ quantity: 6, name: "Apple", type: "Fruit" ],
        [ 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的错误检测。在使用动态类型时要有清晰完善的文档注释