轉載: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的錯誤檢測。在使用動態類型時要有清晰完善的文檔註釋