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 ''




沒有留言:

張貼留言