2018年8月8日 星期三

build 生命週期和監聽方法 (Gradle 4.x 一)

官網連結

.初始化階段:
Gradle 支持單項目和多項目構建
在初始化階段,Gradle 確定哪些項目將參與構建,並為每個項目創建一個 Project 實例

.設定階段:
在此階段,配置專案物件
將執行構建的所有專案的構建腳本
Gradle 1.4 引入了一種稱為按需配置的孵化選擇功能。在此模式下,Gradle 僅配置相關專案

.執行階段:
Gradle 確定要在執行期間創建和設定任務的子集,然後子集傳遞給 gradle 命令和當前目錄。最後 Gradle 執行每個選定的任務


其中設定的部分還有四種狀態,Internal、Incubating、Public、Deprecated,最後兩個大家都知道,就不說明了
官網連結

※Internal:
內部功能不是為公共使用而設計的,僅供Gradle本身使用
可以在任何時候以任何方式改變而不另行通知
所以,儘量避免使用此類功能
內部功能可能演變為公共功能

※Incubating:
孵化狀態下的功能可能會在將來的 Gradle 版本中發生變化,直到它不再孵化為止
孵化的所有方法/屬性/類都使用 @Incubating

gradle 預設 import 的套件非常多,看官網連結

※
this.gradle.addProjectEvaluationListener(new ProjectEvaluationListener() {
    @Override
    void beforeEvaluate(Project project) {
        println '無'
    }
    
    @Override
    void afterEvaluate(Project project, ProjectState projectState) {
        println '設定後,執行前1'
    }
})
    
this.gradle.addListener(new BuildListener() {
    @Override
    void buildStarted(Gradle gradle) {
        println '無'
    }
    
    @Override
    void settingsEvaluated(Settings settings) {
        println '無'
    }
    
    @Override
    void projectsLoaded(Gradle gradle) {
        println '無'
    }
    
    @Override
    void projectsEvaluated(Gradle gradle) {
        println '設定後,執行前4-1'
    }
    
    @Override
    void buildFinished(BuildResult buildResult) {
        println '生命週期完成1-2'
    }
})
    
this.gradle.addBuildListener(new BuildListener() {
    @Override
    void buildStarted(Gradle gradle) {
        println '無'
    }
    
    @Override
    void settingsEvaluated(Settings settings) {
        println '無'
    }
    
    @Override
    void projectsLoaded(Gradle gradle) {
        println '無'
    }
    
    @Override
    void projectsEvaluated(Gradle gradle) {
        println '設定後,執行前4-2'
    }
    
    @Override
    void buildFinished(BuildResult buildResult) {
        println '生命週期完成1-1'
    }
})
    
this.beforeEvaluate {
    println '無'
}
    
this.gradle.beforeProject {
    println '無'
}
    
this.afterEvaluate {
    println '設定後,執行前3'
}
    
this.gradle.afterProject {
    println '設定後,執行前2'
}
    
this.gradle.buildFinished {
    println '生命週期完成2'
}

※在 build.gradle 同層,有一個檔案叫 settings.gradle,再裡面寫 println,會發現它是最早印出來的,因為初始化就在這裡

2018年8月2日 星期四

JSON、XML、IO (groovy 2.x 六)

官網 JSON 教學、官網 XML 教學 、官網 IO 教學


※JSON

package script
    
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
    
def json = JsonOutput.toJson([1, 'a', 3.2, [a: 1]]) // 轉成 JSON 字串
println json
// println JsonOutput.prettyPrint(json) // 印出格式化後的 JSON 字串
List<Object> a = new JsonSlurper().parse(json.getBytes()) // 將 JSON 字串轉成物件
println a
    
class Animal {
    Integer id
    String name
}
    
def animalList = [ new Animal(id: 1, name: 'tiger'),
                   new Animal(id: 2, name: 'turtle') ]
def animalJson = JsonOutput.toJson(animalList)
println animalJson
// println JsonOutput.prettyPrint(animalJson)
List<Animal> la = new JsonSlurper().parse(animalJson.getBytes())
println la

※



※XML

final def xml = '''<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        <h1>YEAH</h1>
        <ul>
            <li class=\'aaa\'>AA</li>
            <li class=\'bbb\'>BB</li>
            <li class=\'ccc\'>CC</li>
        </ul>
        <ul>
            <li class=\'add\'>AD</li>
            <li class=\'aee\'>AE</li>
            <li class=\'aff\'>AF</li>
        </ul>
    </body>    
</html>
'''

※


※
def rtn = new XmlSlurper().parseText(xml) // 根只能一個,也就是最外層的 html 只能有一個
println rtn.name() // html
println rtn.body[0].h1[0].text() // YEAH
println rtn.body.h1.text() // YEAH
println rtn.body.ul.li[1].text() // B
println rtn.body.ul.li[1]["@class"] // bbb
println rtn.body.ul.li[1].'@class' // bbb
println rtn.body.ul.li[1].@class // bbb
println rtn.body.ul.li.text() // AABBCCADAEAF
println rtn.body.ul.li.@class // aaabbbcccaddaeeaff
    
def list = []
rtn.body.ul.each { ul ->
    ul.li.each {
        list << it
    }
    println()
}
println list // [AA, BB, CC, AD, AE, AF]

※取屬性有三種寫法



※depthFirst、children()

def filter = rtn.body.ul.depthFirst().findAll {
    it.@class in ['aaa', 'add']
}/*.collect { //如果想輸出非 text,可再加 collect
    it.@class
}*/
println "filter= $filter" [AA, AD]
    
def filter0 = rtn.body.ul.children().findAll {
    println it
}
println filter0
    
    
def filter1 = rtn.body.ul.'*'.find {
    println it.@class // aaa
    println it.name() // li
    it
}
println "filter1= $filter1" // AA
    
def filter2 = rtn.body.ul.'**'.find {
    println it.@class //
    println it.name() // ul
    it
}
println "filter2= $filter2" // AABBCC
    
def filter3 = rtn.body.ul.'*'.findAll {
    println it.@class // aaa\r\nli\r\nbbb\r\nli...
    println it.name() // li
    it
}
println "filter3= $filter3" // AABBCCADAEAF
    
def filter4 = rtn.body.ul.'**'.findAll {
    println it.@class //
    println it.name() // ul\r\naaa\r\nli\r\nbbb...
    it
}
println "filter4= $filter4" // [AABBCC, AA, BB, CC, ADAEAF, AD, AE, AF]

※depthFirst() 可用 '**' 代替;children() 可用 '*' 代替



※產生 XML

import groovy.xml.MarkupBuilder
    
//<aaa display="true">
//  <bbb xxx="x" ppp="1">123</bbb>
//  <bbb xxx="o" ppp="2">
//      456
//      <ddd>sub</ddd>
//  </bbb>
//  <ccc xxx="x" ppp="3">789</ccc>
//  <ccc xxx="o" ppp="4">012</ccc>
//</aaa>
    
def sw = new StringWriter()
def xml = new MarkupBuilder(sw)
xml.aaa(display: 'true') {
    bbb(xxx: 'x', ppp: '1', value: 123)
    bbb(xxx: 'o', ppp: '2', value: 456) {
        ddd(value: 'sub')
    }
    ccc(xxx: 'x', ppp: '3', value: 789)
    ccc(xxx: 'o', ppp: '4', value: '012')
}
println sw

※


※使用bean,產生XML

class Aaa {
    String display
    List<Bbb> b
    List<Ccc> c
}
    
class Bbb {
    String xxx
    String ppp
    String value
    Ddd d
}
    
class Ccc {
    String xxx
    String ppp
    String value
}
    
class Ddd {
    String value
}
    
def d = new Ddd(value: 'sub')
def b1 = new Bbb(xxx: 'x', ppp: 1, value: 123)
def b2 = new Bbb(xxx: 'o', ppp: 2, value: 456, d: d)
def c1 = new Bbb(xxx: 'x', ppp: 3, value: 789)
def c2 = new Bbb(xxx: 'o', ppp: 4, value: '012')
def aaa = new Aaa(display: true, b: [b1, b2], c: [c1, c2])
    
def sw = new StringWriter()
def xml = new MarkupBuilder(sw)
    
xml.aaa(display: aaa.display) {
    for (def bc : aaa) {
        /*
        for (def bd : bc.b) {
            bbb(xxx: bd.xxx, ppp: bd.ppp, value: bd.value) {
                if (bd.d != null) {
                    ddd(value: bd.d.value)
                }
            }
        }
        */
    
        aaa.b.each { ab ->
            bbb(xxx: ab.xxx, ppp: ab.ppp, value: ab.value) {
                ab.d.each {
                    ddd(value: it.value)
                }
            }
        }
    
        bc.c.each {
            ccc(xxx: it.xxx, ppp: it.ppp, value: it.value)
        }
    }
}
println sw

※註解是我一開始用閉包時,ddd 這一層包不進去 bbb,所以只好使用 for 迴圈的方式,後來發現 aaa.b 不用 it 才有辦法做到



※IO

假設已有一個檔案 xxx.txt,內容是:
123
abc
456
def
※
def file = new File('xxx.txt')
println file.getText()
println file.readLines() // [123, abc, 456, def]
println file.readLines('UTF-8') // [123, abc, 456, def]
    
file.each {
    println it
}
    
file.eachLine { content, num ->
    println "$num $content"
}
    
println file.withReader {
    def buffer = new char[2]
    it.read(buffer)
    buffer
}
    
def out = new File('ooo.txt')
out.withWriter {
    String enter = System.getProperty('line.separator')
    it.append('abc').append(enter).append('123').append(enter)
    it.append('def').append(enter).append('456')
}
    
def copyFile(File src, File dest) {
    if (!src.exists()) {
        src.createNewFile()
    }
    
    src.withReader { r ->
        def lines = r.readLines()
        dest.withWriter { w ->
            lines.eachWithIndex { String content, int i ->
                w.append("$i $content" + System.getProperty('line.separator'))
            }
        }
    }
    println 'copy success'
}
copyFile(new File('ooo.txt'), new File('copy.txt'))

※

2018年8月1日 星期三

呼叫方法的流程 (groovy 2.x 五)

官網連結
當你呼叫一個方法,你沒寫過這個方法,而且也不是 jar 檔裡的方法時,java 會出錯;但 groovy 不一定會出錯,是有一定的流程的

官網的圖,一開始就分有沒有實作 GroovyInterceptable,有實作就呼叫 invokeMethod,但如果沒有 invokeMethod,官網就沒畫了

※我參考官網的圖,有一個部分我並不十分了解,以下說明
Method exists in MetaClass or class,就這個不是很懂
Property exists in MetaClass or class 且還要是閉包才會呼叫,因為 property 是使用在屬性的,而閉包算是比較特殊的

以上這兩個我試的結果,感覺是兩個合起來,所以我認為是「閉包方法或 class」,這兩個好像沒有誰一定會先執行,我有等 30 分鐘再執行,有可能是另外一個執行

所以以下我就以這兩個合併的前提寫了以下的代碼,看圖除了 exception 外,有 5 個結果,但 invokeMethod 重複了,這要看有沒有實作 GroovyInterceptable 而定,所以實際上有 4 個結果


※有實作 GroovyInterceptable

class MyGroovy implements GroovyInterceptable {
    //  @Override
    //  Object invokeMethod(String methodName, Object args) {
    //      "1.方法名:${methodName},參數:${args}"
    //  }
    
    //  def abc(String p1, Object p2) {
    //        "2-1.參數一:${p1},參數二:${p2}"
    //  }
    
    def methodMissing(String methodName, Object args) {
        "3.方法名:${methodName},參數:${args}"
    }
    
    static void main(String[] args) {
    //  MyGroovy.metaClass.abc = { p1, p2 ->
    //      "2-2.參數一:${p1},參數二:${p2}"
    //  }
        MyGroovy m = new MyGroovy()
        println m.abc('xyz', 123)
    }
}

※全部註解打開,會先執行1,1如果註解,就會執行2,依此類推

※有2-1、2-2 這種的,不一定是 2-1 先執行,也有可能 2-2 先執行


※沒實作 GroovyInterceptable

class MyGroovy2 {
    //  def abc(String p1, Object p2) {
    //      "1-1.參數一:${p1},參數二:${p2}"
    //  }
    
    //  def methodMissing(String methodName, Object args) {
    //      "2.方法名:${methodName},參數:${args}"
    //  }
    
    @Override
    Object invokeMethod(String methodName, Object args) {
        "3.方法名:${methodName},參數:${args}"
    }
    
    static void main(String[] args) {
    //  MyGroovy2.metaClass.abc = { p1, p2 ->
    //      "1-2.參數一:${p1},參數二:${p2}"
    //  }
        
        MyGroovy2 m2 = new MyGroovy2()
        println m2.abc('xyz', 123)
    }
}

※


以上主要是有個類別是 final 類,如 String,沒辦法繼承它,也沒辦法改它,所以可以使用這個功能來新增

※測試

class Animal {
    static void main(String... aaa) {
        // ExpandoMetaClass.enableGlobally() // 據說 1.8.1 版就不需要也能成功了
        // new Animal().nonStaticMethod() // 還沒動態加載,所以找不到方法
    
        Animal.metaClass.constructor = { String s ->  // = 可改成 <<
            println "String constructor"
        } << { Integer i -> // 使用 << 可以增加 overloading 方法,= 不可以
            println "Integer constructor"
        }
    
        Animal.metaClass.static.staticMethod = { // = 可改成 <<
            println 'static method'
        }
    
        Animal.metaClass.nonStaticMethod = { // = 可改成 <<
            println 'nonStatic method'
        }
    
        Animal a = new Animal(8)
        Animal.staticMethod() // 不能使用 call()
        new Animal().nonStaticMethod() // 不能使用 call()
    }
}

※在新增方法後,在別的地方使用,還是沒有新增的方法,所以有了 ExpandoMetaClass.enableGlobally(),在新增方法前呼叫即可,但我沒試出來,一直都可以,找了網路上的文章,聽說是 1.8.1 不需要的樣子


2018年7月31日 星期二

this、owner、delegate (Groovy 2.x 四)

※
class MyGroovy {
    class InnerGroovy {
        static def staticClouser = {
            println "a ${this}" // 閉包定義的類
            println "b ${owner}" // 閉包定義的類或物件
            println "c ${delegate}" // 預設和 owner 一樣
        }
    
        def static methodStaticClouser() {
            def xxx = {
                println "d ${this}"
                println "e ${owner}"
                println "f ${delegate}"
            }
            xxx.call()
        }
    }
    
    static void main(def args) {
        MyGroovy.InnerGroovy.staticClouser.call()
        MyGroovy.InnerGroovy.methodStaticClouser()
    }
}

※結果:
a class MyGroovy
b class MyGroovy$InnerGroovy
c class MyGroovy$InnerGroovy
d class MyGroovy
e class MyGroovy$InnerGroovy
f class MyGroovy$InnerGroovy


※
class Animal{}
    
class MyGroovy {
    static def staticClouser = {
        println "a ${this}" // 閉包定義的類
        println "b ${owner}" // 閉包定義的類或物件
        println "c ${delegate}" // 預設和 owner 一樣
    }
    
    static def methodStaticClouser() {
        def xxx = {
            println "d ${this}"
            println "e ${owner}"
            println "f ${delegate}"
        }
        xxx.delegate = new Animal()
        xxx.call()
    }
    
    static void main(def args) {
        MyGroovy.staticClouser.call()
        MyGroovy.methodStaticClouser()
    }
}

※結果:
a class MyGroovy
b class MyGroovy
c class MyGroovy
d class MyGroovy
e class MyGroovy
f Animal@3745e5c6


※
class Animal {
    String name
}
    
class MyGroovy {
    Integer id
    String name
    Double price
    
    def show = {
        "編號${id},名稱${name},價錢為${price}"
    }
    
    String display() {
        println show.call()
    }
    
    static void main(String[] args) {
        MyGroovy m1 = new MyGroovy(price: 50.2, name: '西洋棋', id: 1)
        MyGroovy m2 = new MyGroovy(name: '象棋', id: 2)
        Animal a = new Animal(name: '老虎')
        m1.display()
        m1.show.delegate = a
        m1.show.resolveStrategy = Closure.DELEGATE_FIRST // 預設為 0,也就是 OWNER_FIRST
        m1.display()
        m2.display()
    }
}

※有點像 javascript 的 call、apply、bind

2018年7月30日 星期一

閉包與相關方法 (Groovy 2.x 三)

※數字與閉包的相關方法

※upto、downto

package script
    
def up(int n) {
    def sum = 0
    // 1.upto(n) { num -> println num }
    // 1.upto(n, {num -> rtn += num})
    1.upto(n) { num ->
        sum += num
    }
    sum
}
println up(10)
    
    
def down(int n) {
    // 10.downto(n) { num -> println num }
    // 10.downto(n, {num -> n += num})
    10.downto(n) { num -> n += num }
    n
}
println down(0)

※因為在參數使用 def 會有 cannot infer argument type ...,所以改成了 int

※閉包在最後一個參數,可以將閉包獨立拿到 () 後面


※times

def add(n) {
    def rtn = 0
    n = ++n // 因為底層的 size 是 <,不是 <=
    // n.times({ num -> rtn += num })
    n.times { num -> rtn += num }
    rtn
}
println add(10)

※



※字串與閉包的相關方法

※each、eachLine

// each
def s = 'abc'
def rtn = s.each {
    st -> print st.multiply(2) // aabbcc
}
println '\r\n' + rtn // abc
    
    
// eachLine
def s2 = '''abc
xyz
OGC
'''
def rt = s2.eachLine(0, {
    st, i -> println "${i} = ${st}"
})
println rt // null

※eachLine 第一個參數預設為0,index的意思,可以改變起始數字


※find、findAll、any、every、collect、collectReplacements

final def s = 'a1b2c3'
println s.find {
    st -> !st.isNumber() // a,找到第一個不是數字的,沒有是 null
}
    
println s.findAll {
    st -> !st.isNumber() // [a, b, c],找到全部都不是數字的,沒有是 null
}
    
println s.any {
    st -> st.isNumber() // true,其中一個是數字就是 true
}
    
println s.every {
    st -> st.isNumber() // false,全部是數字才是 true
}
    
println s.collect { // 循環每一個 index
    st -> st.center(3, "&") // [&a&, &1&, &b&, &2&, &c&, &3&]
}
    
println s.collectReplacements { // 循環每一個 index 後,判斷是否取代
    it == 'a' ? 'A' : it
}
    
    
// public static Object find(Object self, Closure closure) {...}

※最後一行註解的部分是原碼,第一個參數是自己去點的內容,可以假裝沒看到



※集合與閉包的相關方法

※
def list = ['xxx', 'x', 'xxxxx', 'xx', 'xxxx']
    
list.sort {
    it.size() // it -$gt; return it.size()
}
println list // [x, xx, xxx, xxxx, xxxxx]
    
def list2 = [9, -5, 2, 7]
println list2.min() // -5
println list2.max { // 9
    Math.abs(it)
}
    
println list2.count {
    it % 2 == 0 // 偶數有幾個
}

※


※排序

def list = [1, -7, 2, 3, -6, 8, -4, 5]
/*
Collections.sort(list) { a, b ->
    if (a == b) {
        0
    } else {
        Math.abs(a) < Math.abs(b) ? -1 : 1
    }
}
*/
    
list.sort() { a, b ->
    if (a == b) {
        0
    } else {
        Math.abs(a) < Math.abs(b) ? -1 : 1
    }
}
println list

※

String 方法、邏輯控制 (Groovy 2.x 二)

※String 方法

package script
    
String x = 'xyz';
String r1 = x.charAt(1)
String r2 = x[1..2]
println x // xyz
println r1 // y
println r2 // yz
    
String c = x.center(6, '?') // 沒有第二個參數,預設是空格
println x // xyz
println c // ?xyz??
    
String p = x.padLeft(6, '?')
println x // xyz
println p // ???xyz
    
println 'abc'
println 'abc' > 'abd' // 前二個一樣,第三個 ascii 99 > 100 嗎? false
    
println 'axbxc'.minus('x') // abxc
println 'axbxc' - 'x' // abxc
    
println 'axbxc'.plus('x') // axbxcx
println 'axbxc' + 'x' // axbxcx
    
println 'abc'.reverse() // 從後往前讀,cba
println 'abc'.capitalize() // 首字母大寫,Abc
println 'ABC'.uncapitalize() // 首字母小寫,aBC
    
println ''.isBlank() // true
println ' '.isBlank() // true
println ''.isEmpty() // true
println ' '.isEmpty() // false
    
println Integer.MAX_VALUE // 2147483647
println '2147483648'.isInteger() // false
println '2147483648'.isLong() // true
println '2147483648'.isFloat() // true
println '2147483648'.isDouble() // true
println '2147483648'.isBigInteger() // true
println '2147483648'.isNumber() // 會呼叫 isBigDecimal(),true
println '2147483648'.isBigDecimal() // true
// 沒有 isByte isShort isCharacter isBoolean
    
println '2147483647'.toInteger() // 超過會報錯
// 沒有 toByte

※



※if

if(new ArrayList<>()) {
    println 'true'
} else { // 0、0.0、null、''、new空物件、
    println 'false'
}

※java 一定要是 boolean 才可以


※不支援範圍操作

int i = 59
if(i == 1..59) {
    println '不及格'
} else {
    println '及格' // 永遠都是及格
}

※


※switch

Integer a = 7
switch (a){
    case 7:
        println 'a'
        break
    
    case '7':
        println 'b'
        break
    
    case Number.class:
        println 'c'
        break
    
    case Integer.class:
        println 'd'
        break
    
    default:
        println 'e'
}

※java 的 switch 型別要全部一樣才可以,且只能是 char 和 int,java 7 又多個 String

※a b c d 都會被呼叫到,看誰寫前面就先印


※
def list = [1,2,3]
switch (list){
    case ArrayList:
        println 'a'
        break
    
    case [1,2,3]: // 相當於是 new,不可能進來
        println 'b'
        break
    
    case List.class:
        println 'c'
        break
    
    default:
        println 'd'
}

※switch 可以放其他東西,可以有類似 instanceof 的寫法


※支援範圍操作

def i = 61
switch (i){
    case 0..59:
        println '不及格'
        break
    
    case 60..100:
        println '及格'
        break
    
    default:
        println '超過範圍'
}

※


※迴圈

def sum = 0
for (def i in 1..10) { // in 寫「:」也行
    sum += i
}
println sum

※


※
for (def m : [a: 1, b: 2, c: 3]) { // in 寫「:」也行
    print "${m.key} "
    println m.value
}

※List 、Set 就直接拿來用,而 Map,使用內鍵的 key 和 value 即可

2018年7月25日 星期三

基本概念 (Groovy 2.x 一)

主要寫的是 Java 和 Groovy 的差異,和 Java 一樣就不寫了

※Eclipse 安裝環境

Help -> About Eclipse 查自己的版本
然後到這裡,標題為Releases,複製對應自己版本的 Release Update Site
然後回到 Eclipse,選 Help - > Install New Software,然後如下操作:


※選左上角的 Add 後,會跳出一個視窗, Name 隨便打,Location 將剛剛複製的貼進去


※詳細差在哪我不知道,反正我是全選了,安裝要一段時間,安裝好後會提示重啟 Eclipse



※IntelliJ、Android Studio

安裝好 IntelliJ 或 Android Studio 就有環境可測試了(較舊的沒有),如下圖:


※但是這只是測試用而已,如果想使用 Groovy 的專案,還要去官網下載 groovy 的 SDK 來用,如果安裝好了,可以 New -> Groovy Script,如 xxx.Ooo,這將會創建一個 package 為 xxx,而 Ooo.groovy 為檔名

※要注意 jvm 版本和安裝時預設雖然有勾環境變數,但會有「~」的路徑,我沒試過可不可以,反正我是改成正確的路徑,最後 New -> Project 如下設定即可(但 Android Studio 我不知道):

如果忘記選 Groovy library,還可以如下設定:
※有成功才會在左邊的 External Libraries 有 groovy-xxx的東西,沒有就要在右邊的 Add Framework Support 裡面去勾選了,右邊的是按 TestGroovy 按右鍵出來的



※預設import

java.io.*
java.lang.*
java.math.BigDecimal
java.math.BigInteger
java.net.*
java.util.*
groovy.lang.*
groovy.util.*
    
import static Calendar.getInstance as now

※import static 比 Java 多個 as 的別名功能


※class 的省略

class Person {
    String name
    
    String greet(String otherPerson) {
        Integer i = 88;
        def j = 11;
        println i + j
        "Hello ${otherPerson}"
    }
    
    static void main(String... args){
        println new Person().greet("xxxxxxxxxxxxxxxxxx")
    }
}

※最後可不加「;」
1.回傳值可不用寫 return 關鍵字
2.宣告多一個 def,如 def i = 0;、def s = 'xxx',轉換後為 Object

/* class */
3.如果屬性前面沒有修飾子(同package),轉換後的 class 其實會幫我們加上 private,並給 setter/getter,但如果自己加 private,就不會提供 setter/getter,不管是不是 static 都會這樣
4.方法、class、建構子,修飾子不寫和寫 public 一樣,而方法不管是不是 static 也是一樣,所以 main 方法可以不寫 public
5.雖然已安裝 java8,還是不能寫 lambda 表達式和 reference method(::)

/* interface */
6.只能定義 public 和不寫,但轉換後都是不寫的
7.雖然已安裝 java8,也是不能在裡面定義 static 方法和 default 關鍵字,我試的是 2.5.1 版本
如果一定要使用 java8 的 default,groovy 有一種很像 interface 的 Type,叫做 trait,轉換後也是 interface,可參考官網的教學,或者這篇簡而有力的教學

8.System.out.println 只寫 println 即可,() 可省略(在 groovy 中,只要是有參數的都可省略),但 println 是 groovy 的,可按 ctrl + 滑鼠左鍵進去看
9.預設改 private 後,轉換後也確實是 private,但還是抓的到
10.看 groovy 幫我們的代碼轉成什麼樣子
※在 out 資料夾裡有轉變後的 class,注意如果有修改,要執行完才會重新生成
可以看到都會實作一個叫 GroovyObject 的介面


※六種字串

// 1.單引號
println 'ab' == 'a' + 'b' // true,這個才是和java的 「"」一樣
    
// 2.雙引號
def x = 'xxx'
println "x is ${x}" /* x is xxx,多了打變數的功能,「{}」可省略
類似ES6的「`」,想打「$」,用跳脫字元「\」*/
    
// 3.三個單引號
def o = '''a
b
c
d'''
println o // 有換行效果,類似HTML的 <pre>
    
// 4.三個雙引號
def o = """a
b
${x}
d""" // 三個「'」加上抓變數的功能
    
// 5.正斜線
/.../
用「/」包起來,可以有以上的所有功能
    
// 6.錢符號正斜線
$/.../$
和/../很像,差在跳脫字元的不同,這個跳脫字元是「$」,其他都是「\」
    
println /\//; // /
println $///$; // /
    
println /$/; // $
println $/$$/$; // $

※使用雙引號,而且有使用到變數,會自動轉為 org.codehaus.groovy.runtime.GStringImpl,可以印出 .class 查看

※如果有一個方法定義參數為 String,GString 可以傳的進來,但相反不行


※方法

package script
    
final def me = "me!"
final def test = "look ${me}"
    
println "1 + 1 = ${1+1}" // 1 + 1 = 2
println echo('xxx') // a xxx
println echo("xxx") // a xxx
println echo(test) // b look me!
    
String echo(m) {
    "a ${m}"
}
    
String echo(GString m) {
    "b ${m}"
}
    
String echo(String m) {
    "c ${m}"
}

※a 方法省略了 def 關鍵字,而這三個方法都省略了 return 關鍵字

※a 方法在沒有明確的型態對應時才會呼叫的到,如將 c 刪除就呼叫的到

※如果 test 變數沒有使用到變數(${me}),那會是 String,有用到變數才是 GString

※groovy 就算寫 void,還是會有回傳值 null,但 void 關鍵字不能省略



※方法的括號可省略

echo 'xxx'
    
def echo(String m) {
    println "${m}"
}
    
getList([1, 2, 3])
getList Arrays.asList(1, 2, 3)
getList Stream.of(1, 2, 3).collect(Collectors.toList())
    
getArray(['a', 'b', 'c'] as String[])
getArray 'a', 'b', 'c'
String[] array = new String[3]
array[0] = 'a'
array[1] = 'b'
array[2] = 'c'
getArray array
    
getMap([a: 1, b: 2, c: 3])
getMap(a: 1, b: 2, c: 3)
getMap a: 1, b: 2, c: 3
    
def getList(List<Integer> list) {
    println "a ${list}"
}
    
def getArray(String[] array) {
    println "b ${array}"
}
    
def getMap(Map<String, Integer> map) {
    println "c ${map}"
}

※只有完全沒參數才一定要(),否則都可省略,但要注意閉包可以提出去的問題,假設有個方法有兩個參數,最後一個是閉包,提出去時不能省略(),想省略只能想在()裡面

※集合使用到 groovy 的寫法時,只有 Map 可省略() 和 []

※給陣列傳參數時,不能寫 new String[] {xxx, xxx} 的方式,因為 groovy 會以為是閉包

※集合

//   List、Set、Array 
def list = [1, 2, 3]
println list
println list instanceof List
println list instanceof LinkedList
println list.size()
list << 4 // 除了add方法外,用這個符號也能增加元素
    
def linkList = [1, 2, 3] as LinkedList
println linkList instanceof List
println linkList instanceof LinkedList
    
def set = [3, 2, 3] as Set
println set instanceof Set
    
String[] sArray =['Ananas', 'Banana', 'Kiwi']
def sArray =['Ananas', 'Banana', 'Kiwi'] as String[]    
def intArray = [1, 2, 3] as int[]
    
    
//   Map 
def map = [:] // 宣告空的 map
println map // [:]
println map.isEmpty() // true
map."a" = "aaa" // key 有沒有用「"」包起來都是可以的
map << [c:"ccc"]
map.put("d", "ddd") // 這個和之前一樣,key還是要用「"」包起來
println map // [a:aaa, c:ccc, d:ddd]
println map.a // aaa
println map['a'] // aaa
println map["a"] // aaa
    
def m = [b:"bbb"] // 非空的 map
println m // [b:bbb]
println m.b // bbb
m.xxx = [b:"bbb"]
println m.toMapString() // [b:bbb, xxx:[b:bbb]],Map 中的 Map
println m.xxx.b // bbb
println m.class // null,因為沒有class這個key
println m.getClass() // class java.util.LinkedHashMap

※增加元素都可以用「<<」


※運算符、三元運算子

println 2 ** 10 // 1024,2的10次方
    
def f = 2
println f ** 5 // 32,2的5次方
println f **= 5 // 32,f = f ** 5
    
def x = 1;
println x == 1 ?: "sucess" // 「?:」不能分開,?:之間沒寫預設是 true

※?:最後的 else 一定要寫


※基本型態的後綴

Java 只有 L、F、D 來代表 Long、Float、Double,大小寫都可以
Groovy 多兩個 I 和 G,一樣大小寫都可以,i 表示 Integer;G 代表有兩個 BigInteger、BigDecimal
例:
def long = 999L
def xxx = 555I
def ooo = 548G

※最好用大寫,避免和數字混淆

※groovy 中沒有基本型態,雖然可以宣告,但用 class 去查看,會發現都是 Wrapper 類別

※使用 def 時,預設宣告整數為 Integer,有小數點的為 BigDecimal(就算是1.0也是一樣)


※?.

String result = null
println result?.toUpperCase()

※可防止 NullPointerException,回傳 null


※@field

class User {
    String name = 'xxx'
    String getName() {
        println 'ooooooooooooo'
        this.name
    }
}
println new User().@name

※可以直接專取屬性,不加 @ 會呼叫 getter

※&method

def str = 'xxx'
def result = str.&toUpperCase //一定要用def接
println result()
    
def xxx(String str) { str.toLowerCase()}
def xxx(Integer x) { 2 * x }
def ref = this.&xxx
println ref('xOx') // xox
println ref(123) // 246

※使用「.&」宣告完,可以當方法呼叫,但要注意 &後面的方法沒有 ()

class Ooo {
    int count = 0
    def xxx() {
        return ++this.count
    }
}
    
def ref = new Ooo().&xxx
println ref() // 1
println ref() // 2

※這個有點像 javascript 的閉包功能

※*.

class Car {
    String make
    String model
}
def cars = [
    new Car(make: 'Peugeot', model: '508'),
    new Car(make: 'Renault', model: 'Clio')
]
    
def makes = cars*.make
println makes == ['Peugeot', 'Renault'] // true
println makes instanceof List // true

※只取集合的某個屬性


※延伸方法參數

int function(int x, int y, int z) {
    x*y+z
}
    
def args = [4,5,6]
println function(*args) // 26

※


※延伸操作-集合

def items = [4,5,2]
def list = [1,2,3,*items,6]
println list == [1,2,3,4,5,2,6] // true
    
def set = [1,2,3,*items,6] as Set
println set == [1,2,3,4,5,6] as Set // true
    
def m1 = [c:3, d:4]
def map = [a:1, b:2, *:m1]
println map == [a:1, b:2, c:3, d:4]

※


※範圍操作

def range = 0..3
println range // [0, 1, 2, 3]
println (1..3).collect() //  [1, 2, 3]
println (1..<3).collect() // [1, 2],只有小於,也只能寫在後面
println ('a'..'d').collect() //[a, b, c, d]
println range.contains(3) // true,包括3這個元素嗎?
println range.contains('3') // false,包括3這個元素嗎?
println range.contains(4) // false
println range.from // 0,第一個元素
println range.to // 3,最後一個元素
    
    
def r = 0..30
def n = r.by(3)
n.each {
    print "${it} " // 0 3 6 9 12 15 18 21 24 27 30
}
    
/* 簡化
(0..30).by(3).each {
    print "${it} "
}
*/

※<和3中間可以空格

※Range 為 List 的子類,都是 interface,Range 有四個實作類 EmptyRange, IntRange, NumberRange, ObjectRange


※太空船操作

println 1 <=> 1 //  0,相等
println 1 <=> 3 //  -1,左邊小於右邊
println 3 <=> 1  // 1,左邊大於右邊
println 'a' <=> 'z'//  -1
    
println 1.compareTo(1) // 0
println 1.compareTo(3) // -1
println 3.compareTo(1) // 1
println 'a'.compareTo('z') // -25

※可能 <==> 長得像太空船吧!

※1就是 true;-1就是 false


※正則

package script
    
import java.util.regex.Matcher
import java.util.regex.Pattern
    
    
def p1 = ~/^foo.*/ // 以 foo 開頭的
def p2 = ~'.*foo$' // 以 foo 結尾的
def p3 = ~"(foo){2}" // 剛好是兩個 foo 的
def p4 = ~$/foo/$ // 只能是 foo 的
println p1 instanceof Pattern
println p1.matcher('foo123').matches()
println p2.matcher('123foo').matches()
println p3.matcher('foofoo').matches()
println p4.matcher('foo').matches()
    
    
def text = "xxx"
def m = text =~ /xxx/
println m instanceof Matcher
println m.matches()
if (m.matches()) { // 官網直接判斷m而已,說會呼叫find(),但我試的結果永遠為true
    println '有匹配到'
} else {
    println '沒匹配到'
}
println m.matches() ? 'o' : 'x'
    
    
def t = 'ooo'
def mat = t ==~ /ooo/
println mat instanceof Boolean
println mat
if (mat) {
    println '有匹配到'
} else {
    println '沒匹配到'
}
println mat ?:false

※主要就是 ~/.../ 為 Pattern; =~ 為 Matcher; ==~ 為 Boolean

※/.../ 後面和 javascript 不一樣,沒有什麼 gim 的


※範圍操作-集合

def list = [0,1,2,3,4]
println list[2] == 2 // true
list[2] = 4
println list[0..2] == [0,1,4] // true
list[0..2] = [6,6,6]
println list == [6,6,6,3,4] // true

※


※閉包

def closure1 = {
    -> println "xxx" // xxx
}
closure1.call() // call 可以省略
    
def closure2 = { i, p ->
    println "${i}, ${p}" // 5, ooo
}
closure2(5, "ooo")
    
def closure3 = {
    println it // 是我, it為內鍵變數
}
closure3("是我")

※一定要用 def 宣告

※使用 -> 將參數和實作隔開,但如果沒有參數,可以省略 ->

※和 java8 的 lambda 不太一樣,參數永遠沒有「()」

※如果只有一個參數,可以使用 it 這個內鍵變數

※參數的型態和 lambda 一樣,通通可以省略,相當於是 def,但如果有寫,如 String,那就要乖乖傳 String 進去

※一定有回傳值,沒有就是 null,像 println 回傳 void,所以是 null,return 關鍵字可以不寫,所以假設最後一行寫 'xxx',那回傳的就是 xxx

※return 可以不寫在最後一行,但 return 之後的程式碼不會執行


※閉包的循環

[1,2,3,4,5].each { print "${it}"} // 12345
println ''
['a':1,'b':2,'c':3].each { print "${it}-"} // a=1-b=2-c=3-
println ''
['a':1,'b':2,'c':3].each { print it.key+":"+it.value} // a:1b:2c:3
println ''

※