Groovy 之基礎語法,閉包

基本類型

​ 其實在 Groovy 中是沒有基本類型的,例如下所示:

int x = 10;
println(x.class)

​ 打印結果如下:

class java.lang.Integer

​ 可以看到他是一個 Integer 對象。其實在底層編譯的時候已經將 int 裝箱成了 對象。

變量的定義

​ groovy 定義變量有兩種方式,分別是 強類型定義 和 若類型定義,強類型定義和 java 中的意義,但是弱類型定義是什麼呢,如下:

//強類型定義方式
int x = 10;
println(x.class)

//弱類型定義方式
def x1 = 1;
println(x1.class)
def x2 = 3.14;
println(x2.class)
def x3 = "Android"
println(x3.class)

​ 結果如下:

class java.lang.Integer
class java.lang.Integer
class java.math.BigDecimal
class java.lang.String

​ 第二行 是大數據類型

​ 其實弱類型定義就是不指定具體的類型。通過值來確定它到底是什麼類型。用 def 來定義

​ 如何選擇呢?如果在本類中使用,則建議使用 def 類型。但是別的類也要用到這個變量。則儘量使用強類型定義;def 聲明的變量類型可以隨時改變,如 x1 是 Integer 。如果給 x1 賦值爲 String,這樣他就會變爲 String。正是這個原因所以纔不建議在 別的類使用這個變量時定義爲 def。

字符串

​ 常用的定義方式:

//1,通過 單引號定義String ,和雙引號沒有任何區別 ,轉義 '單引號 \'String \''
def name = '單引號 String'
println(name)


//2,通過三個單引號定義String ,但定義的格式進行輸出,
def name1 = '''第一行
第二行
第三行'''
println(name1)

// 3,雙引號 ,可擴展字符串,擴展後使用的不是String,而是GString類
def name2 = "雙引號"
def hello = "hello ${name2}"
println(name2)
println(hello.class)

//可擴展字符串使用 ,可以擴展任意的表達式
def sum = "sum = ${5 - 6 - 6 + 12}"
println(sum)

def result = echo(sum)
println(result)
String echo(String message){
    return message;
}

通過上面這幾種方式可以 定義字符串,其實一般最常用的就是 第三種了。注意擴展後的字符串就不是 String。而是 GString。不信的話可以打印看一下。

字符串方法

字符串的方法
1,第一種就是String類原有的方法
2,第二種則是 DefaultGroovyMethods ,是 Groovy 對所有對象的擴展
3,第三種是 StringGroovyMethods ,繼承自 DefaultGroovyMethods 。

重點要看的是 StringGroovyMethods 。他繼承自第二種,第一種是 String,比較熟悉。

首先定義一個字符串:

String str = "哈哈哈"
  • 填充

    println str.center(8, 'a') //填充: aa哈哈哈aaa
    println str.padLeft(8, 'a') //填充:aaaaa哈哈哈
    
  • 比較

    println str > "嘻嘻嘻" //通過運算符比較字符串大小 :false
    
  • 索引

    println str.getAt(0) //下標爲0 :哈
    println str[0]  //通過類似於數組的方式來獲取 :哈
    println str[0..1] //指定範圍 :哈哈
    
  • 減法

    println str.minus("哈哈") //減法 : 哈 	--減去兩個剩一個 。
    println str - "哈哈";
    
  • 倒序

    println str.reverse()
    
  • 首字母大寫

    println str.capitalize()
    
  • 判斷 str 是否爲數字類型

    println str.isNumber()
    
  • 轉爲基本數據類型

    println str.toInteger()
    println str.toBoolean()
    println str.toDouble()
    
  • 其實方法還有很多,這裏就不一一列舉了。

    可查看文檔

控制語句

​ switch:groovy 中的 switch 和 java 中的有明顯的不同,它可以接受任意的數據類型。如下:

// switch ,可以傳入任意類型的數據
def x = 1.23
def result;
switch (x) {
    case '哈哈哈哈':
        result = '0'
        break
    case '嘻嘻':
        result = '1'
        break
    case [4, 5, 6, 'inlist']: //列表
        result = '2'
        break
    case 12..30: //範圍
        result = '3'
        break
    case Integer:
        result = '4'
        break
    case BigDecimal:
        result = '5'
        break
    default:
        result = 'defalut'
        break
}
println result //5

​ for 循環

// 對範圍的 for 循環
def sum = 0
for (i in 0..9) {
    sum += i
}
println sum

//對 list 進行循環
sum = 0
for (i in [1, 2, 3, 4, 5, 6, 7, 8, 7]) {
    sum += i
}
println sum

//對 map 進行循環
sum = 0;
for (i in ['one': 1, 'two': 2, "three": 3]) {
    sum += i.value
}
println sum; 
// 45
// 43
// 6

閉包

基礎詳解

​ 閉包是一個開放的,匿名的代碼塊,可以帶參數,返回一個值。可以通過一個變量引用到他,也可以傳遞給別人進行處理。

  • 閉包的創建和調用

    def clouser = {
        println "hello groovy"
    }
    //兩種方式調用閉包
    clouser.call()	// hello groovy
    clouser()		// hello groovy
    
  • 創建帶參數的閉包和調用

    def clouser = {
            //箭頭前面是參數,後面是閉包體
        String name, int age ->
            println "hello groovy ${name} -- ${age}"
    }
    //調用帶參數閉包
    clouser.call("張三", 32)  // hello groovy 張三 -- 32
    clouser("李四", 23)		//hello groovy 李四 -- 23
    
  • 默認參數

    //每一個閉包都有一個默認的 it 參數,如果不想使用,則顯示的定義即可。
    def clo = {
        println "hello ${it}"
    }
    clo.call("李四")	 //hello 李四
    
  • 閉包返回值

    def clouser = {
            //箭頭前面是參數,後面是閉包體
        String name, int age ->
            return "hello groovy ${name} -- ${age}"
    }
    //調用帶參數閉包
    println clouser.call("張三", 32) //hello groovy 張三 -- 32
    

使用詳解

​ 基本類型結合使用:


  • 階乘

    int x = 5
    println fab(x)
    //求 number 的階乘
    int fab(int number) {
        int reslut = 1
        //調用 upto 方法,傳入 number 和 閉包 ,閉包中有一個 num 參數
        1.upto(number, { num -> reslut *= num })
        return reslut
    }
    //結果:120
    

    爲什麼要這樣寫呢?看一下源碼秒懂!

    //打眼一看這裏需要三個參數,我們只傳了兩個?,注意:1.upto()中的 1,代表的是第一個參數,
    public static void upto(Number self, Number to, @ClosureParams(FirstParam.class) Closure closure) {
    		//拿到開始的位置和結束的位置
            int self1 = self.intValue();
            int to1 = to.intValue();
            //小於等於
            if (self1 <= to1) {
                for (int i = self1; i <= to1; i++) {
                	//調用傳入的閉包,將i 傳進去,從這裏就可以知道我們寫的閉包爲啥要一個 num 的參數了。
                    closure.call(i);
                }
            } else
                throw new GroovyRuntimeException("The argument (" + to +
                        ") to upto() cannot be less than the value (" + self + ") it's called on.");
        }
    
  • 階乘2

    int fab2(int number) {
        int result = 1
        number.downto(1) {
            num -> result *= num
        }
        return result
    }
    //結果:120
    

    你知道 downto 方法傳入了幾個參數嗎。。。。沒錯,是3個,number是一個,1 是一個,括號外面的閉包是一個。閉包寫在括號外面和寫在裏面效果完全一樣。這個是倒着來的。具體的可以查看源碼。

  • 累加

    int cal(int number) {
        int result
        // times 只有兩個參數,所以可以使用這種方式來寫 ,具體的實現可以看源碼,非常簡單
        number.times {
            num -> result += num
        }
        return result;
    }
    

字符串的結合使用


定義一個字符串:

String str = "the 2 and 3 is 5"
  • each 遍歷

    str.each {
        String temp ->
            print temp  //the 2 and 3 is 5
    }
    //看一下 each 方法的源碼
    public static <T> T each(T self, Closure closure) {
        	//第一個是迭代器,第二個則是閉包了
            each(InvokerHelper.asIterator(self), closure);
            return self;
        }
    
    public static <T> Iterator<T> each(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
        	//遍歷 字符串,
            while (self.hasNext()) {
                Object arg = self.next();
                //調用閉包,傳入了一個參數,從這裏就可以知道我們在定義閉包的時候需要加一個參數
                closure.call(arg);
            }
            return self;
        }
    
  • 查找符合條件的第一個

    println str.find {
        String s ->
        	//返回 boolean 。是否符合條件,這裏的條件爲:是否是數字
            s.isNumber() 
    }
    //結果:2 ,第一個符合結果的是2,看一下源碼
        public static Object find(Object self, Closure closure) {
            //將閉包弄成了 boolean 類型的
            BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
            //迭代
            for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
                Object value = iter.next();
                //調用閉包,如果返回 true。則表示滿足條件,接着就將 value 返回,然後就退出了。不在執行
                if (bcw.call(value)) {
                    return value;
                }
            }
            return null;
        }
    
    //查找全部
    println str.findAll {
        String s ->
            return s.isNumber()
    }
    //結果:[2, 3, 5] ,查看源碼可知找到後會存在集合中,最後進行返回
    
  • 判斷是否包含數字

    println str.any {
        String s ->
            return s.isNumber() // true
    }
    //源碼可自行查看
    
  • 字符串是否每一項都爲數字

    println str.every {
        String s ->
            s.isNumber() //false
    }
    //源碼
    public static boolean every(Object self, Closure predicate) {
          return every(InvokerHelper.asIterator(self), predicate);
    }
    public static <T> boolean every(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure predicate) {
            BooleanClosureWrapper bcw = new BooleanClosureWrapper(predicate);
            while (self.hasNext()) {
                //是否滿足閉包中的條件
                if (!bcw.call(self.next())) {
                    return false;
                }
            }
        	//循環完成後表示都滿足條件,返回true
            return true;
        }
    
  • 將字符串轉爲集合

    println str.collect {
        //將 字符轉換大寫
        it.toUpperCase()
    }
    //查看源碼
     public static <T> List<T> collect(Object self, Closure<T> transform) {
         	//這裏創建了一個新的集合
          return (List<T>) collect(self, new ArrayList<T>(), transform);
     }
    public static <T> Collection<T> collect(Object self, Collection<T> collector, Closure<? extends T> transform) {
        	//給字符串加上迭代器
            return collect(InvokerHelper.asIterator(self), collector, transform);
    }
      public static <S,T> Collection<T> collect(Iterator<S> self, Collection<T> collector, @ClosureParams(FirstParam.FirstGenericType.class) Closure<? extends T> transform) {
            while (self.hasNext()) {
                //調用閉包中的方法,將返回的結果保存到集合中
                collector.add(transform.call(self.next()));
            }
          	//返回集合
            return collector;
        }
    

進階詳解

  • 閉包關鍵變量 this,owner,delegate

    this:閉包定義處的類

    owner :代表閉包定義處的類或者對象。因爲閉包是可以嵌套的,當閉包嵌套的時候,owner 就是閉包,而不是所處的類

    delegate:任意一個對象,默認與 owner 一樣

    看一個例子:

    def clouser = {
        println this  //閉包定義處的類
        println owner  //代表閉包定義處的類或者對象,因爲閉包是可以嵌套的,如果 閉包中有嵌套,則 owner 代表的就是閉包
        println delegate //任意一個對象。默認與 owner 一致
    }
    clouser.call()
    //結果:
    variable.ClooseStudy@5acf93bb
    variable.ClooseStudy@5acf93bb
    variable.ClooseStudy@5acf93bb
    

    可以看到這三個結果是一樣的。因爲這裏的閉包沒有嵌套,所以 owner 是定義閉包處的類,delegate 則和 owner 一致

    接着看:

    //定義了一個內部類
    class PerSon {
        //靜態閉包
        def static classClouser = {
            println "Class  " + this
            println "Class  " + owner
            println "Class  " + delegate
        }
    	//靜態方法
        def static say() {
            def classClouser = {
                println "method  " + this
                println "method  " + owner
                println "method  " + delegate
            }
            classClouser.call()
        }
    }
    //結果:
    Class  class variable.PerSon
    Class  class variable.PerSon
    Class  class variable.PerSon
    method  class variable.PerSon
    method  class variable.PerSon
    method  class variable.PerSon
    

    其實這個和上面的都差不多,這裏的 this 就是內部類了,而 owner 因爲不是嵌套,所以也是 Person。delegate 就不用說了。注意:這裏的結果後面沒有內存地址是因爲 閉包和方法都是靜態的。

    接着看:

    //閉包中定義一個閉包
    def nestClouser = {
        def innerClouser = {
            println "innerClouser  " + this
            println "innerClouser  " + owner
            println "innerClouser  " + delegate
        }
        innerClouser.call()
    }
    nestClouser.call()
    //結果:
    innerClouser  variable.ClooseStudy@6cd28fa7
    innerClouser  variable.ClooseStudy$_run_closure2@f0c8a99
    innerClouser  variable.ClooseStudy$_run_closure2@f0c8a99
    

    這裏的 this 是當前類,但是 owner 不一樣了,可以看到他在後面拼接了一點地址,這個就是外層閉包的對象。delegate 默認和 owner 一樣,所以他們兩個一樣。你可以打印一下 nestClouser 的地址看一下是否和 owner 一樣。。

    如果修改了 delegate 的值,則他和 owner 就不一樣了。注意:this 和 owner 是不能被修改的,只有 delegate 可以修改

閉包委託策略

​ 首先看一段代碼

class Student {
    String name
    def pretty = {
        "My name is ${name}"
    }

    String toString() {
        pretty.call()
    }
}


class Teacher {
    String name
}

def student = new Student(name: '張三')
def teacher = new Teacher(name: "老師")
println student.toString()

​ 定義了兩個類,一個學生和一個老師。

​ 如果調用 student 的 toString 方法,會打印什麼信息呢?

My name is 張三

​ 可以看到打印的是張三。那如果要打印 Teacher 的 name 呢?這個時候就要用到 委託策略了

student.pretty.delegate = teacher
println student.toString()

​ 將 teacher 傳給 student 的閉包的 delegate 。這個時候在輸出一下,你會發現沒有任何改變。。。

​ 我們少改了一個東西, 默認的委託策略是 OWNER_FIRST ,即從 owner 中開始尋找對象 。owner 指向的是當前類 student。所以纔沒有發生任何變化。所以我們要修改委託策略:

student.pretty.delegate = teacher
student.pretty.resolveStrategy = Closure.DELEGATE_FIRST //修改委託策略
println student.toString()
//My name is 老師

​ 修改爲 DELEGATE_FIRST 後會從 delegate 中開始尋找,因爲上面指定了 delegate 是 teacher ,所以輸出就變了

​ 修改 Teacher 中的 name 字段名字,如下:

class Teacher {
    String name1
}
def teacher = new Teacher(name1: "老師")

​ 這個時候在運行會發現 運行的結果是 張三 ,因爲 student 中的閉包裏面用的是 name,而不是 name1,所以找不到 name 後就會尋找當前類的 name。

​ 修改一下委託策略,如下:

student.pretty.resolveStrategy = Closure.DELEGATE_ONLY

​ 接着在運行一下會報錯:

name for class: variable.Teacher Possible solutions: name1

​ 在 Teacher 中 沒有找到 name ,所以報錯。這個策略只會從 delegate 中尋找,而不會從當前類中找

​ 委託策略一共有四種:

public static final int OWNER_FIRST = 0;
public static final int DELEGATE_FIRST = 1;
public static final int OWNER_ONLY = 2;
public static final int DELEGATE_ONLY = 3;

​ 默認的就是 OWNER_FIRST ,即從 owner 中開始尋找。

列表

//列表
def list = [1, 23, 324, -5, 6]  // groovy 中定義的方式,和上面的完全一樣 ,並且添加元素。
println list.class

//排序
list.sort()
println list
list.sort {
    a, b ->
        a > b ? 0 : -1
}
println list
//按字符串長度排序
def strList = ['a', 'ab', 'abc', 'aaaaa', 'b', 'cadfa', 'ace']
strList.sort {
    it ->
        return it.size()
}
println strList

//查找被 2 整除
println list.findAll() {
    return it % 2 == 0
}
// 判斷是否列表中元素是否都能夠被 2 整除
println list.every {
    return it % 2 == 0;
}
println list.min() //最小值
println list.max() //最大值
println list.min { Math.abs(it) } //絕對值 最小值
println list.max { Math.abs(it) } //絕對值 最小值

println list.count {
    return it % 2  //統計,有多少偶數
}

//添加
list.add(5)
list.leftShift(4)
println list + [4, 5]

//刪除
list.remove(0) //刪除下標爲 0 的元素
println list
list.remove((Object) 4) //刪除內容爲 4 的元素
println list
println list.removeAt(0) //刪除指定位置元素,並返回此元素
list.removeElement(0) //刪除
println list - [1, 23] //刪除 1 和 23

上面是列表的定義和常用的方法

有沒有感覺和數組的定義一樣。當然數組的定義方式也變了,如下:

//數組
def array = [1, 3, 4, 5] as int[]   
// groovy 中 數組的定義
int[] array2 = [13, 4, 5, 6]  //強類型數組定義

Map

//定義
def map = [red: '紅色', yellow: '黃色', black: '黑色']

//查找
println map.get('red')
println map['yellow']
println map.black
println map.find {
    it.getValue().equals("紅色")
}
//計數
println map.count {
    it.value.equals('黃色')
}
println map.findAll {
    return it.key.equals('red') //key 爲 red
}.collect {
    it.value  //key 爲 red 的value
}

//分組
println map.groupBy {
    return it.value.equals('紅色') ? '紅色' : '其他顏色'
}


//添加
map.put('blue', '綠色')
map.green = '灰色'
map.data = [a: 0, b: 1];
println map
println map.getClass() //默認是 LinkerHash , 在定義的時候 通過 as HashMap 可轉爲 HashMap

//遍歷
map.each {
    def m ->
        println m.key + "----" + m.value
}
//帶下表的變量
map.eachWithIndex { Map.Entry<String, String> entry, int i ->
    println "index ${i}" + entry.key + "----" + entry.value
}
map.each {
    key, value ->
        println key + "----" + value
}

//排序
println map.sort()
println map.sort {
    s1, s2 ->
        return s1.key < s2.key ? 0 : -1
}

以上爲最常見的使用

範圍

//定義
def range = 1..29

println range[0]//獲取
println range.contains(15) //是否包含
println range.from //開始
println range.to    //結束

//遍歷
range.each {
    print it
}
println ""
for (i in range) {
    print i
}
println ""

//在 switch 中使用
def sh = {
    int x ->
        def result = 0;
        switch (x) {
            case 0..10:
                result = 1;
                break
            case 10..20:
                result = 2;
                break
            case 20..30:
                result = 3;
                break
        }
        return result;
}
println sh.call(20)
public interface Range<T extends Comparable> extends List<T> {
	......
}

其實 Range 是 繼承自 List。所以 List 有的方法他都有。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章