1. 程式人生 > >Gradle系列之一 Groovy語法精講

Gradle系列之一 Groovy語法精講

Gradle技術之一 Groovy語法精講

gradle指令碼是基於groovy語言開發的,想要學好gradle必須先要對groovy有一個基本的認識

1. Groovy特點

  • groovy是一種DSL語言,所謂的DSL語言,就是專門針對某一特定領域的語言,專精而不專廣
  • 是一種基於JVM的開發語言,也是編譯成class位元組碼檔案
  • 結合和Python,Ruby,PHP等語言的特性,寫指令碼非常強大
  • Groovy可以與Java完美結合,而且可以使用Java所有的類庫
  • 語法上支援動態型別,閉包等新語言的特性
  • 支援面向過程和麵向物件程式設計

2. Groovy語法精講

1 變數的型別和定義

  • -變數的型別分兩種
  1. 基本型別:和java的基本型別一樣,int,long,double,float,boolean,byte,String
    注意Groovy中,這些基本型別實際上都會被系統裝箱為對應的物件型別,比如int就會被自動裝箱為Integer

  2. 物件型別:和Java的一樣
  • - 變數的定義
  1. 強型別定義:如 int a=10 ,指定變數的型別
  2. 弱型別定義:如 def b = 5,系統根據後面的值自動判斷b是什麼型別
  • - Groovy語法小點
  1. 語句後面可以不帶分號,也可以帶分號,如定義一個變數 int a = 10
  2. 輸出一句話,直接println就行了,圓括號也可以省略,如 println "hello,world"
  3. 定義變數或者函式可以使用關鍵字def ,也可以按照Java的語法格式定義,如def name="tom"

如下面的程式碼

//強定義型別
int x = 10
println x.class

double y = 3.14
println y.class


//弱型別定義
def a = 10
println a.class

def b = 3.14
println b.class

def name = "tom"
println name.class
輸出如下 :
class java.lang.Integer
class java.lang.Double
class java.lang.Integer
class java.math.BigDecimal
class java.lang.String

注意:在Groovy中,所有定義的基本型別都會被系統自動裝箱為相應的包裝型別

3 Groovy中的String相關

1. 字串的定義

第一種方式:使用單引號

//使用單引號定義的字串,裡面的內容是不可以改變的
//等價於 Java中的 String name = "hello world";
def name = 'hello world'
println name 
println name.class

//輸出
hello world
class java.lang.String

第二種方式:三個單引號

//之間的內容包含格式,裡面的內容是什麼格式的,顯示出來的就是什麼格式的
def content = '''\
line one
line two
line three
println content'''
println content.class
    
//輸出
line one
line two
line three
class java.lang.String

第三種方式:使用雙引號

//上面兩種方式定義的字串是不可改變的,但是這種方式定義的字元是可以擴充套件的,裡面可以包含其它的變數
def name = "android"
def str = "hello $name"
println str
println str.class

//輸出
hello android
class org.codehaus.groovy.runtime.GStringImpl

注意:第三種方式定義的字串,裡面帶有 $ 符號拼接的字元型別就是GString型別,其它的都還是String型別,由此可知,Groovy已經擴充套件了我們的字串,可原始碼可知org.codehaus.groovy.runtime.GStringImpl就是繼承自GString,如下:

public class GStringImpl extends GString {
    private String[] strings;
    ....
    

4 GString的用法

4.1 GString特性

特性1 可以拼任意表達式

def num = "3 + 5 = ${3 + 5}" //後面可以跟任意表達式
println num 
//輸出
3 + 5 = 8

問:Groovy中擴展出了GString,也就是可擴充套件的字串,那麼Groovy中兩種字串型別,即:String,GString,使用過程中需要注意什麼呢?如果一個方法中的引數是String,那麼可不可以傳GString 的呢?
答:沒有一點關係,使用是對開發者透明的,完全不用管,可以互相使用。看下面一個函式,引數需要String,但是傳的是GString,看看輸出結果都是正常的,編譯器也沒有報錯,所以這兩種字串是不用關心型別的,是可以隨便用的

特性2 兩種字串型別可以互用

def num = "3 + 5 = ${3 + 5}" //後面可以跟任意表達式

//傳的是GString型別的
def result = show(num)
println result


//方法接收的是String型別的
String show(String message){
    return message
}

//輸出
3 + 5 = 8

//由此可以知道,GString,String在開發過程中不用刻意關心型別

4.1 String常用方法

  • 普通的方法
  • 帶閉包的方法 (後面講閉包的時候講)

  • -普通的方法
    1. center()
    2. padLeft,padRight
    3. 可以直接比較,如下
def str = 'groovy'
def str2 = "hello"
def str3 = 'hello'
println str > str2
println str == str2
println str2 == str3

//輸出
false
false
true

//也可以直接使用索引,如
println str[0]
//輸出
g


//可以傳入一個範圍,如
println str[0..1]
//輸出
gr


//減法,把str1中包含的str2減掉,如
def str1 = 'hello,world'
def str2 = 'hello'
println str1 - str2
println str1.minus(str2)
//輸出
,world
,world


//字串反轉
def str3 = 'hello'
println str3.reverse()
//輸出
olleh


//所有單詞首字母大寫
def str4 = 'hello world'
println str4.capitalize()
//輸出
Hello world


//判斷是否是數字型別的字串,如
def str5 = '234'
println str5.isNumber()
//輸出
true


//轉化成數字,如
def str6 = '123'
println str6
//輸出
123

5 Groovy中的邏輯控制

邏輯控制語句有三種

  • 順序控制:單步往下執行
  • 條件邏輯:if/else 和 switch/case
  • 迴圈邏輯:while 和 for

這些邏輯控制是和Java中的一樣的,但是又擴充套件了一些功能
其中if/elsewhile和Java中的用法是一樣的,switch/casefor增加了一些擴充套件,我們使用下面的程式碼來演示這兩種擴充套件的用法,程式碼如下:

//switch語句
def x = 3.14
def result
switch (x){
    case 'test':
        result = 'test'    //字串
        break
    case [4,5,6,'test']:    //列表
        result = 'list'
        break
    case 3..11:
        result = 'range'    //範圍
        break
    case Integer:
        result = 'Integer'  //型別
        break
    case BigDecimal:
        result = 'BigDecimal'
        break
    default:
        result = 'default'
        break
}

println result
//輸出
BigDecimal

Groovy中的switch中可以是任何型別,資料

//1 對範圍的for迴圈
def sum = 0
for (i in 0..3){
    sum += i
}
println sum
//輸出
6


//2 對List的迴圈
def sum = 0
for (i in [1,2,3,4,5,6,7,8,9]){
    sum += i
}
println sum
//輸出
45

//3 對Map的迴圈
def sum = 0
for (i in ['tom':1,'jim':2,'xiaoming':3]){
    sum += i.value
    println i.key
}
println sum
//輸出
tom
jim
xiaoming
6

6 Groovy中的閉包

Groovy中的閉包很強大,下面主要從三個方向講解閉包

  • 1 閉包的基礎詳解
  • 2 閉包的使用詳解
  • 3 閉包的進階詳解

6.1 閉包的基礎

6.1.1 閉包的概念:閉包就是一段程式碼塊,可以命名可以被呼叫,使用和方法類似

//1 定義一個閉包,閉包的定義就是這麼簡單
//  就是一段程式碼塊,可以命名,可以被呼叫
def closer = {
    println "hello groovy"
}

//2 閉包的兩種呼叫方式
//  建議使用第一種方式呼叫,這樣不會和方法的呼叫混淆
closer.call()
closer()
//輸出
hello groovy
hello groovy

6.1.2 閉包的引數:普通引數和隱式引數
閉包是可以傳引數的,有點像Java中的lambda表示式,如下程式碼

//1 定義一個無參的閉包
//  -> 也可以省略, -> 前面的是引數,-> 後面的是閉包體
def closer = {
    -> println "hello groovy"
}

//使用
closer.call()
closer()
//2 定義一個有參的閉包, 引數是name
def closer = {
   String name -> println "hello $name"
}

closer.call()   //引數可以不傳,不傳就是null
closer.call('world')
closer('android')

閉包還有一個隱式的引數 it ,類似類中的this,如果定義閉包沒有明確指定引數,但是呼叫了閉包傳了引數,那麼就可以在程式碼中使用 it 關鍵字,如下

//3 定義一個無參的閉包, 使用隱式的引數關鍵字 it
def closer = {
    println "hello $it"
}

closer.call()   //引數可以不傳,不傳就是null
closer.call('world')
closer('android')

6.1.3 閉包的返回值:總是有返回值的
閉包總是有返回值的,如果沒有明確的return,返回值就決定在最後一句程式碼,如下

//4 閉包的返回值,如果沒有return,最後一句話就是返回值
def closer = {
    "hello world"
}

//返回值是"hello world"
def result = closer.call()
println result
//輸出
hello world

6.2 閉包的使用

閉包的使用主要從以下幾個方面講

  • 1 與基本型別的結合使用
  • 2 與String結合使用
  • 3 與資料結構結合使用
  • 4 與檔案等結合使用

6.1.1 與基本型別的結合使用

//用來求指定number的階乘
//求5的階乘 1*2*3*4*5 = 120
int res = fab(5)
println res

int fab(int number){
    int result = 1
    1.upto(number,{
        num -> result *= num
    })

    return result
}

這段程式碼是不是很簡潔,主要的應該都在upto()方法中,我們來看一下upto()方法的原始碼,如下

  public static void upto(Number self, Number to, @ClosureParams(FirstParam.class) Closure closure) {
        int self1 = self.intValue();    //初始值,就是1
        int to1 = to.intValue();        //結束值,就是5
        if (self1 <= to1) {//如果初始值小於結束值,就迴圈
            //開始從 self1到to1的迴圈,把每一個值都交給最後一個閉包來處理
            //而在我們的閉包中,又把每一項乘的結果儲存在了result變數中
            for (int i = self1; i <= to1; i++) {
                closure.call(i);
            }
        } else
            throw new GroovyRuntimeException("The argument (" + to +
                    ") to upto() cannot be less than the value (" + self + ") it's called on.");
    }

注意,Groovy方法中,如果最後一個引數是閉包,那麼圓括號是可以省略的,我們用downto()方法來演示省略圓括號的用法

既然有upto()方法,那麼肯定也有downto()方法了,我們來看一下downto()方法的使用,

最後一個引數是閉包的話,方法的圓括號可以省略

//求5的階乘 1*2*3*4*5 = 120
int res = fab2(5)
println res

//注意,最後一個引數是閉包的話,方法的圓括號可以省略
int fab2(int number){
    int result = 1
    number.downto(1){
        num -> result *= num
    }

    return result
}

同樣,downto()的原始碼如下:

//與upto()原始碼類似
 public static void downto(Number self, Number to, @ClosureParams(FirstParam.class) Closure closure) {
        int self1 = self.intValue();    //初始值是5
        int to1 = to.intValue();        //結束值是1
        if (self1 >= to1) {
            for (int i = self1; i >= to1; i--) {
                closure.call(i);
            }
        } else
            throw new GroovyRuntimeException("The argument (" + to +
                    ") to downto() cannot be greater than the value (" + self + ") it's called on.");
    }

再看一個例子,方法只有一個閉包引數的,如下,求和

//求10以內的和
int res = fab3(10)
println res

int fab3(int number){
    int result = 0
    
    //圓括號省略,直接跟一個閉包
    number.times {
        num -> result += num
    }

    return result
}

//輸出
45

同樣,times()方法的原始碼如下:

  public static void times(Number self, @ClosureParams(value=SimpleType.class,options="int")  Closure closure) {
        for (int i = 0, size = self.intValue(); i < size; i++) {
            closure.call(i);
            if (closure.getDirective() == Closure.DONE) {
                break;
            }
        }
    }

從上面幾個例子可以知道,我們在寫閉包的時候,有時候並不知道需要傳入什麼樣的引數,這個時候就只能去檢視原始碼或者官方文件了,所以檢視原始碼和官方文件是一個比較好的習慣

6.1.2 與String的結合使用

//each()方法的使用

String str = 'hello world'
//把每一個字元都傳給閉包
str.each {
    temp -> print temp
}
//輸出
hello world
//find()方法的使用

String str = 'tom has 63 books and 3 apples'

//查詢第一個是數字的字元,閉包必須返回一個boolean值
def result = str.find {
    word -> word.isNumber()
}

println result
//輸出
6

可以看下find()函式是如何工作的,find()的原始碼如下 :

  public static Object find(Object self, Closure closure) {
        //把閉包包裝成了一個BooleanClosureWrapper物件 
        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;
    }

同理還有findAll()方法,用法如下

String str = 'tom has 63 books and 3 apples'

//查詢所有是數字的字元
def result = str.findAll {
    word -> word.isNumber()
}

println result
//輸出
[6, 3, 3]
//any()方法的使用

String str = 'tom has 63 books and 3 apples'

//判斷字串中是否包含數字
def result = str.any {
    temp -> temp.isNumber()
}

println result
//輸出
true

any()方法的工作原理如下:

  public static boolean any(Object self, Closure closure) {
        BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
        
            //只要閉包條件滿足,就返回true,很簡單吧
            if (bcw.call(iter.next())) return true;
        }
        return false;
    }
//every()的用法 

String str = 'tom has 63 books and 3 apples'

//判斷字串中是否全是數字
def result = str.every {
    temp -> temp.isNumber()
}

println result
//輸出
false

every()方法的原理如下:

  public static boolean every(Object self, Closure closure) {
        BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
        
            //只要有一項不滿足,立馬返回
            if (!bcw.call(iter.next())) {
                return false;
            }
        }
        return true;
    }

any()方法是隻要有一項滿足就返回, every()方法是隻要有一項不滿足就返回,正好相反

//collect()的使用

String str = 'hello world'

//將字串轉化成大寫
def result = str.collect {
    temp -> temp.toUpperCase()
}

println result
//輸出
[H, E, L, L, O,  , W, O, R, L, D]

collect()方法的工作原理如下:

public static <T> List<T> collect(Object self, Closure<T> transform) {
        //注意第二個引數,new了一個新的ArrayList()
        return (List<T>) collect(self, new ArrayList<T>(), transform);
    }

 public static <T> Collection<T> collect(Object self, Collection<T> collector, Closure<? extends T> transform) {
        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); ) {
        
            //將閉包處理過的結果都新增到新的ArrayList中
            collector.add(transform.call(iter.next()));
        }
        
        //返回閉包處理過的結果
        return collector;
    }

6.1 閉包的進階使用

  • 1 閉包的關鍵字變數 this, owner, delegate
  • 2 閉包的委託策略

6.1.1 閉包的三個關鍵字 this, owner, delegate

首先先打印出這三個變數,看看輸出什麼

def myScript = {
    println "myScript this=" + this
    println "myScript owner=" + owner
    println "myScript delegate=" + delegate
}

myScript.call()
//輸出
myScript [email protected]
myScript [email protected]
myScript [email protected]

可以看到,這三個代表的是一個意思,那麼這三個關鍵字到底有什麼區別呢?

  • 1 this :代表閉包定義處的類
  • 2 owner :代表閉包定義處的類或者物件
  • 3 delegate:代表任意物件,但是預設是和owner是一樣的

如果在閉包中再定義一個閉包呢,看看這三個關鍵字會打印出什麼

def myScript = {
    println "myScript this=" + this
    println "myScript owner=" + owner
    println "myScript delegate=" + delegate

    def innerCloser = {
        println "innerClouser this =" + this
        println "innerClouser owner=" + owner
        println "innerClouser delegate=" + delegate
    }

    innerCloser.call()
}


myScript.call()
//輸出
myScript [email protected]
myScript [email protected]
myScript [email protected]
innerClouser this [email protected]
innerClouser [email protected]
innerClouser [email protected]

所以,總結下來,只有三句話:

  • 1 this,owner,delegate這三個關鍵字在類和方法中定義時,指向是一樣的
  • 2 在閉包中定義的閉包的時候,ownerdelegate指向的是外面的閉包物件
  • 3 在修改了delegate的時候指向的時候,delegate又和其它兩個不一樣了

如下程式碼,修改了delegate的指向

//定義了一個內部類
class Person{

}

Person p = new Person()

def myScript = {
    println "myScript this=" + this
    println "myScript owner=" + owner
    println "myScript delegate=" + delegate

    def innerCloser = {
        println "innerClouser this =" + this
        println "innerClouser owner=" + owner
        println "innerClouser delegate=" + delegate
    }

    innerCloser.delegate = p

    innerCloser.call()
}


myScript.call()
//輸出
myScript [email protected]
myScript [email protected]
myScript [email protected]
innerClouser this [email protected]
innerClouser [email protected]
innerClouser [email protected]

可以看到delegate指向了Person,不再和owner一樣了

6.1.2 this, owner, delegate的作用,也就是委託策略

用一段程式碼來演示更改委託策略,程式碼如下:


class Student{
    String name

    //定義一個閉包
    def showMe = {
        return "my name is $name"
    }

    @Override
    String toString() {
        return showMe();
    }
}

class Teacher {
    String name

}

def stu = new Student(name : 'tom')
def tea = new Teacher(name : 'Mrs Li')

//1 沒有改變委託策略的情況下
println stu.toString()


//2 改變閉包的委託策略
stu.showMe.delegate = tea
stu.showMe.resolveStrategy = Closure.DELEGATE_FIRST //把委託策略改成先從delegate中找name
println stu.toString()
//輸出
my name is tom
my name is Mrs Li

可以看到,在不改變委託策略的情況下,輸出的是my name is tom

在改變了委託策略下,把閉包的指向改成了 tea,並且把委託策略也改成了Closure.DELEGATE_FIRST ,那麼查詢name的時候,首先就從deleage指向處查詢
所以列印的是my name is Mrs Li

7 Groovy的資料結構

Groovy中主要有3種常用的資料結構。列表,對映,範圍
這三個資料結構比較簡單,主要以程式碼的形式來講,如下

7.1 列表

//1 定義列表

//1.1 Java的定義方式
def list = new ArrayList()

//1.2 groovy中定義
def list2 = []          //定義一個空的列表
def list3 = [1,2,3,4]   //定義一個非空的列表

println list2.class
println list3.class
//輸出
class java.util.ArrayList
class java.util.ArrayList

可以看到,直接這樣定義的就是一個ArrayList

//1.2 定義陣列

//在groovy中使用as關鍵字定義陣列,注意和列表的區別
def array = [1,2,3,4] as int[]

//或者使用強型別的定義方式
int[] array2 = [1,2,3]

由於在Groovy中,列表和陣列的定義方式類似,使用也相似,一般我們只使用列表就夠了

//1.3 列表的排序
def list = [-7,5,-3,9,4,0]
list.sort() //直接一句話就排序了(預設從小到大)
println list

//輸出
[-7, -3, 0, 4, 5, 9]
//1.4 列表的排序
def list = [-7,5,-3,9,4,0]

//按照絕對值從大到小排序,閉包中傳入的是排序規則 
list.sort {
    a,b -> a == b ? 0 : Math.abs(a) < Math.abs(b) ? 1 : -1
}

println list
//輸出
[9, -7, 5, 4, -3, 0]
def stringList = ['a','abc','hello','groovy']

//1.5 按照字串的長度大小排序
stringList.sort {
    it -> return it.size()
}

println stringList
//輸出
[a, abc, hello, groovy]
//1.6 查詢列表中的第一個偶數
def list = [-7,5,-3,9,4,0,6]
int result = list.find {
    temp -> return temp % 2 == 0
}

println result
//輸出
4

//1.7 查詢列表中所有小於0的數
def list = [-7,5,-3,9,4,0,6]
def result = list.findAll {
    temp -> return temp < 0
}

println result
//輸出
[-7, -3]
//1.8 統計列表中偶數的個數
def list = [-7,5,-3,9,4,0,6]

int number = list.count {
    return it % 2 == 0
}

println number
//輸出
3

7.2 對映

//1.1 對映的定義
def persons = [tom:'北京',jim:'上海',wendy:'天津']

//1.2 對映的使用
println persons['tom']
println persons.get('jim')
println persons.wendy

//1.3 新增元素
persons.xiaoming = '杭州'
println persons

//1.4 groovy中,可以新增一個複雜的元素,比如新增一個map
persons.complex = [a:1,b:2]
println persons

//輸出
北京
上海
天津
[tom:北京, jim:上海, wendy:天津, xiaoming:杭州]
[tom:北京, jim:上海, wendy:天津, xiaoming:杭州, complex:[a:1, b:2]]

groovy中的map的常用操作

//1.5 對map的遍歷
def students = ['tom':89,'jim':68,'wendy':56,'bob':92]

students.each {
    def student -> println "key is " + student.key + "  value is " + student.value
}

//輸出
key is tom  value is 89
key is jim  value is 68
key is wendy  value is 56
key is bob  value is 92
//1.6 對map的遍歷,帶index
def students = ['tom':89,'jim':68,'wendy':56,'bob':92]

students.eachWithIndex { def student, int index ->
    println "key=${student.key}  value=${student.value} index=${index}"
}

//輸出
key=tom  value=89 index=0
key=jim  value=68 index=1
key=wendy  value=56 index=2
key=bob  value=92 index=3
//1.7 直接對map的遍歷
def students = ['tom': 89, 'jim': 68, 'wendy': 56, 'bob': 92]

students.each {
    key, value -> println "key=${key},value=${value}"
}

//輸出
key=tom,value=89
key=jim,value=68
key=wendy,value=56
key=bob,value=92
//1.8 查詢第一個及格的人
def students = ['tom': 89, 'jim': 68, 'wendy': 56, 'bob': 92]


def result = students.find {
    student -> return student.value > 60
}

println result
//輸出
tom=89
//1.9 查詢所有及格的人
def students = ['tom': 89, 'jim': 68, 'wendy': 56, 'bob': 92]

def result = students.findAll {
    student -> return student.value > 60
}

println result

//輸出
[tom:89, jim:68, bob:92]
//2.0 查詢及格的人數

def students = ['tom': 89, 'jim': 68, 'wendy': 56, 'bob': 92]

def number = students.count {
    student -> student.value > 60
}

println number

//輸出
3
//2.1 查詢所有及格的人的名字
def students = ['tom': 89, 'jim': 68, 'wendy': 56, 'bob': 92]

def names = students.findAll {
    student -> student.value > 60
}.collect {
    return it.key
}

println names

//輸出
[tom, jim, bob]

7.2 範圍

範圍的定義

//1.1 範圍的定義
def range = 1..10

//1.2 範圍的使用
println range[0]            //第一個元素的值
println range.contains(7)   //是否包含7
println range.from          //範圍的起始值
println range.to            //範圍的結束值

//輸出
1
true
1
10

範圍就是這麼簡單,輕量級的list

8 Groovy的面向物件

  • 1 Groovy中類的方法,變數,預設的都是public
  • 2 介面和Java中的幾乎一樣,只有一點區別,就是隻能定義public的方法,不能用protected
//1.1 類的定義
class Person {
    String name
    int age

    //方法的定義
    def addYear(int age){
        this.age += age
    }
    
    @Override
    String toString() {
       return "name=${name} age=${age}"
    }
}
//1.2 例項化一個類的物件
def person = new Person()
def person1 = new Person(name:'tom',age:23)
def person2 = new Person(name:'tom')

println person.toString()
println person1.toString()
println person2.toString()

//輸出
name=null age=0
name=tom age=23
name=tom age=0
//1.3 get/set方法
//無論是直接用 . 還是呼叫get/set方法,最終都是呼叫的get/set方法
//這是和Java不一樣的地方,是編譯器自動為我們生成的get/set
def person = new Person(name:'tom',age:23)

println "name=${person.name} , age=${person.age}"
println "name=${person.getName()}, age=${person.getAge()}"

//輸出
name=tom , age=23
name=tom, age=23

8.1 Groovy中的超程式設計

超程式設計就是程式碼在執行過程中執行的時期,Groovy中,呼叫類的一個方法,如下 :

  • 1 類中是否有此方法,有則呼叫,如果沒有
  • 2 從 MetaClass中查詢是否有此方法,有則呼叫MetaClass中的方法,如果沒有
  • 3 是否重寫了methodMissing()方法,有則呼叫methodMissing()方法,如果沒有
  • 4 是否重寫了invokeMethod()方法,有則invokeMethod()方法,如果沒有,throw MissingMethodException

主要是: 類 --> MetaClass --> methodMissing() --> invokeMethod()

java中如果類中沒有這個方法,就直接報錯了,但是Groovy中,執行時是非常強大的

下面用程式碼來演示:如下

//先定義一個類,還是以剛才的類定義為例
class Person {
    String name
    int age

    def addYear(int age){
        this.age += age
    }

    @Override
    String toString() {
       return "name=${name} age=${age}"
    }
}

def person = new Person(name:'tom',age:23)

//呼叫一個沒有的方法
//會報 groovy.lang.MissingMethodException 異常
person.show()

//輸出
Caught: groovy.lang.MissingMethodException: No signature of method: variable.Person.show() is applicable for argument types: () values: []
//重寫invokeMethod()方法

class Person {
    String name
    int age

    def addYear(int age){
        this.age += age
    }

    @Override
    String toString() {
       return "name=${name} age=${age}"
    }

    //重寫invokeMethod()方法,name是呼叫的方法的名字,args是呼叫方法傳的引數,一個方法找不到的時候,呼叫它代替
    @Override
    Object invokeMethod(String name, Object args) {
        println "the method is ${name},the param is ${args}"
    }
}

//在另一個檔案中,呼叫下面的程式碼 
def person = new Person(name:'tom',age:23)

//呼叫一個沒有的方法
person.show()

//輸出
the method is show,the param is []

所以,由此可知,如果類中沒有方法,但是重寫了invokeMethod()方法,groovy是不會報錯的,會呼叫invokeMethod()方法,可以在這裡面進行提示開發者,沒有這個方法

//重寫methodMissing()方法
class Person {
    String name
    int age

    def addYear(int age){
        this.age += age
    }

    @Override
    String toString() {
       return "name=${name} age=${age}"
    }

    //重寫invokeMethod()方法,name是呼叫的方法的名字,args是呼叫方法傳的引數
    //一個方法找不到的時候,呼叫它代替
    @Override
    Object invokeMethod(String name, Object args) {
        println "the method is ${name},the param is ${args}"
    }

    //這個方法的優先順序要高於invokeMethod(),有了這個方法,將不會再呼叫invokeMethod()方法
    def methodMissing(String name,Object args){
        println "the method ${name} is missing"
    }
}
//在另一個檔案中,呼叫下面的程式碼
def person = new Person(name:'tom',age:23)

//呼叫一個沒有的方法
person.show()

//輸出
the method show is missing

8.2 Groovy中的MetaClass

MetaClass可以在執行時為類動態新增屬性

//為類動態新增一個屬性
Person.metaClass.sex = '男'

def person = new Person(name:'tom',age:23)
println person.sex
//輸出
男
//為類動態新增一個方法
Person.metaClass.showAge = {
    -> println age
}

def person = new Person(name:'tom',age:23)
person.showAge()
//輸出
23
//為類動態新增一個靜態方法
Person.metaClass.static.createPerson = {
    name,age -> new Person(name:name,age:age)
}

def person = Person.createPerson('wendy',44)
println person

//輸出
name=wendy age=44

Groovy這種動態給類新增屬性和方法的特性可以不用通過重寫類而新增類的功能

到現在,Groovy就講完了,Groovy的用法還需要較多的練習才能記得牢
為學習gradle打下堅實的基礎。