阅读 381

Android-Gradle系列-Groovy

前言

Groovy是构建工具Gradle的核心语言,因此,本文为后续学习Gradle做基础。

简介

  • Groovy是基于jvm虚拟机的一种动态脚本语言,完全兼容java,又在此基础上增加了很多动态类型和灵活的特性。
  • 特性:支持动态/静态类型、支持DSL语法特性、无缝衔接Java等

Groovy基础

1. 数据类型

基本数据类型 byte、short、int、long、float、double、char、boolean 包装类 String、Byte、Short、Integer、Long、Float、Double、Char、Boolean

2. 字符串

' ':单引号字符串是java.lang.String类型,不支持表达式,作为字面常量。

" ":双引号字符串在没有使用插值表达式情况下是java.lang.String类型,但如果有表达式使用的话,就是groovy.lang.GString类型。

''' ''':三引号字符串表示多行字符串,不必将字符串分割成几块,也不必用连接符或换行符转义字符来将字符串跨行。

	def groovy1 = 'groovy1'
   	def groovy2 = "groovy2"
  	def groovy3 = "groovy3 插入表达式->${groovy2}"
  	def groovy4 = '''groovy
			 2
			 3'''		
  	println(groovy1)            //输出:groovy1
   	println(groovy1.getClass()) //输出:class java.lang.String
   	println(groovy2)	    //输出:groovy2
   	println(groovy2.getClass()) //输出:class java.lang.String
   	println(groovy3)	    //输出:groovy3 插入表达式->groovy2
   	println(groovy3.getClass()) //输出:class org.codehaus.groovy.runtime.GStringImpl
	println(groovy4)	   //输出:groovy
					   2
                                           3
	println(groovy4.getClass())//输出:class java.lang.String
复制代码

3. 变量

使用def定义变量,动态类型放低了对类型的要求,使语言能够根据上下文来判定变量类型。

	def str = 'str'  
	def num = 123
	println str.getClass() //输出:class java.lang.String
	println num.getClass() //输出:class java.lang.Integer
复制代码

4. 安全导航操作符

检查引用是否空值。

   def foo(str){
   	str?.reverse()
   }
   
   println foo('abc')  //输出:cba
   println foo(null)   //输出:null
复制代码

5. 集合类

5.1 List

   def list = [1,2,3]        
   //打印集合
   println(list)			//输出:[1, 2, 3]
   //集合类型
   println list.getClass()		//输出:class java.util.ArrayList
   //根据下标获取对应位置的值
   println list[0]			//输出:1
   //循环
   list.each { print it }		//输出:123
   //取区间
   println list[0..1]			//输出:[1, 2]
复制代码

5.2. Map

    def maps = ['a': 'A', 'b': 'B', 'c': 'C']
    //类型
    println maps.getClass()   		//输出:class java.util.LinkedHashMap
    //根据key得到value
    println maps['a']			//输出:A
    println maps.b			//输出:B
    //遍历
    maps.each {println "key:$it.key value:$it.value" }
    //输出:key:a value:A
    	   key:b value:B
    	   key:c value:C
复制代码

5.3. Range

范围是一种特殊的列表,由序列中的第一个和最后一个值表示,Range可以是包含或排除。包含范围包括从第一个到最后一个的所有值,而独占范围包括除最后一个之外的所有值。也可以使用表达式来表示范围。

   def range = 0..10
   println range.getClass() //输出:class groovy.lang.IntRange

   1..10  包含范围的示例
   1..<10  独占范围的示例 (开区间)
   'a'..'x'  范围也可以由字符组成
   10..1  范围也可以按降序排列
复制代码

6. 类与方法

构造函数 方法传参(默认值,合并map,Object)

  • 默认会生成getter, setter方法:
  • 可以直接像使用成员变量的方法来自动判断调用getter/setter。
  • 使用'.@'直接访问变量,跳过默认的getter/setter方法调用。
  • 如果没有指定具体参数的类型时,默认推断类型是Object。

7. 函数与闭包

  • Groovy中可以理解为闭包就是可执行的代码块,或匿名函数。闭包在使用上与函数与许多共通之处,但是闭包可以作为一个函数的参数。
  • 默认情况下,闭包能接收一个参数,且参数字段默认使用it。
  • 在箭头前面没有定义参数,这时候闭包不能传入参数。
  • 可以定义多个接收的参数。
  • 使用参数默认值,跟方法使用规则一样。
   def pickEvent(max, block) {
       for (int i = 2; i <= max; i += 2) {
           block(i)
       }
   }
   pickEvent(10) { println(it) }
   输出:2 4 6 8 10

   def foo = { it?.reverse() }
   println(foo("123"))
   输出:321
复制代码

7.1 闭包委托

闭包是一个Closure对象

     public abstract class Closure<V> extends GroovyObjectSupport implements Cloneable, Runnable, GroovyCallable<V>, Serializable {
     	//闭包中的关键对象
     	...
       private Object delegate;
       private Object owner;
       private Object thisObject;
       private int resolveStrategy;
       ...
      }
复制代码
   class Test {
   		//闭包2
       def closure2 = {
           println "this is" + this
           println "owner is" + owner
           println "delegate is" + delegate
       }
		//闭包1
       def closure1 = {
           def closure2 = {
               // this:Test@2b76ff4e,定义它的时候类的this,当它是static时就是class对象
               println "this is" + this
               // owner:Test$_closure1@8c3619e,定义它的时候的类的对象
               println "owner is" + owner
               // delegate:Test$_closure1@8c3619e,默认就是owner, 但是代理可以修改
               println "delegate is" + delegate
           }
           closure2()
       }
   }

   new Test().closure2()
   输出:   同一对象
   this isTest@b2070ef
   owner isTest@b2070ef
   delegate isTest@b2070ef
   
   new Test().closure1()
   输出:   this与其他对象不同
   this isTest@164103c3
   owner isTest$_closure2@51105e92
   delegate isTest$_closure2@51105e92
复制代码
//使用委托
class Test2 {
    def func() {
        println "Test2 func"
    }
}
def func() {
    println "Script func"
}
def closure = {
    func()
}

closure()	//输出为:Script func

//切换代理类为Test2
closure.delegate = new Test2()	
closure()	//输出依旧是:Script func  原因:优先选择的是owner

//策略
//* @see groovy.lang.Closure#DELEGATE_FIRST
//* @see groovy.lang.Closure#DELEGATE_ONLY
//* @see groovy.lang.Closure#OWNER_FIRST
//* @see groovy.lang.Closure#OWNER_ONLY
//* @see groovy.lang.Closure#TO_SELF
//设置策略
closure.resolveStrategy=Closure.DELEGATE_ONLY
closure()		//输出:Test2 func
复制代码

8. Groovy动态特性

  • 动态类型语言:动态类型语言是指在运行期间检查数据类型,也就是说,在用动态类型的语言编程时,可以不用给变量指定数据类型,该语言会在你第一次赋值给变量时,在内部将数据类型记录下来。

    静态类型语言:静态类型语言的数据类型是在编译期间检查的,也就是说在写程序时要声明所有变量的数据类型,C/C++是静态类型语言的典型代表,其他的静态类型语言还有C#、Java等。

  • 使用def定义变量,类型由运行时对其赋值的类型确定,通过这种能力,可以在运行时向类中注入行为,十分灵活,但相应的,如果如果类型不符的话会在运行时报错,所以编码时要更加注意,但也可以使用@TypeChecked强制编译检查。

    class User {
        def username = 'zee'
    }
    def user = new User()
    println user.username.class		//输出:class java.lang.String
    user.username = new Object()
    println user.username.class		//输出:class java.lang.Object
    user.username = 123
    println user.username.class		//输出:class java.lang.Integer
    user.username = new User()
    println user.username.class		//输出:class User
    
    复制代码

9. Groovy元编程

  • MOP (元对象协议)全称:Meta Object Protocol。
  • Java中可以通过反射,在运行时动态的获取类的属性,方法等信息,然后反射调用。但是没法直接做到往内中添加属性、方法和行为。( 需要通过动态字节码技术如ASM、javassist等技术来实现动态的修改class )。
  • Groovy可以使用MOP进行元编程,我们可以基于应用当前的状态,动态的添加或者改变类的方法和行为。比如在某个Groovy类中并没有实现某个方法,这个方法的具体操作由服务器来控制,使用元编程,为这个类动态添加方法,或者替换原来的实现,然后可以进行调用。

9.1. 方法拦截

9.1.1. 实现GroovyInterceptable接口,重写invokeMethod来进行拦截。
          class Target implements GroovyInterceptable {
              def function() {
                  System.out.println 'target#function-invoke'
              }
          
              @Override
              Object invokeMethod(String name, Object args) {
                  if (metaClass.invokeMethod(this, 'respondsTo', name, args)) {
                      System.out.println "$name begin invokeMethod"
                      metaClass.invokeMethod(this, name, args)
                      System.out.println "$name end"
                  }
              }
          }
          new Target().function()
          输出:
          function begin invokeMethod
          target#function-invoke
          function end
复制代码
9.1.2. 使用MetaClass拦截方法,覆盖invokeMethod方法。

使用类的MetaClass,针对的是class对象,所有实例都会被影响。

使用具体实例的MetaClass,只影响当前对象实例。

class Target implements GroovyInterceptable {
    def function() {
        System.out.println 'target#function-invoke'
    }
}

def target = new Target()
//这里只拦截此对象的方法
target.metaClass.function = {
   println 'target#function-invoke-metaClass'
}
target.function()   //输出:target#function-invoke-metaClass

target.metaClass.invokeMethod = {
    String name, Object args ->
        println "$name invoke-metaClass11"
}
target.function()  //输出:function invoke-metaClass11

//这里是拦截了此类的方法
 Target.metaClass.invokeMethod = {
    String name, Object args ->
       println "$name invoke-metaClass22"
}
new Target().function()		//输出:function invoke-metaClass22
复制代码

9.2. MOP方法注入

方法注入:编写代码时知道想要添加到一个或多个类中的方法的名字。利用方法注入,可以动态地向类中添加行为。也可以向任意数目的类中注入一组实现某一特定功能的可服用方法,就像工具函数。有以下几种方式:

9.2.1. 使用分类注入方法。
class TargetUtils {
    static def newFunction(Target self) {
        println "newFunction-invoke"
    }
}

def target = new Target()
use(TargetUtils) {
	target.newFunction()
}
//输出:newFunction-invoke
复制代码
9.2.2. 使用ExpandoMetaClass注入方法。
	class Target {
  	  def function() {
      	System.out.println 'target#function-invoke'
  	  }
	}
    
    def ext = new ExpandoMetaClass(Target)
    ext.newFunction2={
        println 'target#function-invoke-new2'
    }
    ext.initialize()
    //这里也可以给对象的metaClass赋值,仅作用在对象上
    Target.metaClass = ext
	new Target().newFunction2()
	 //输出:target#function-invoke-new2
复制代码
9.2.3. 直接使用类或实例的MetaClass注入方法,实际上最终操作的类型是ExpandoMetaClass。
    Target.metaClass.newFun = {
        println 'target#function-invoke-new'
    }
    new Target().newFun()
    //输出:target#function-invoke-new
复制代码
9.2.4. 使用Mixin注入方法。( 在类中可以使用多个Mixin )
class Helper{
    def help(){
        println "help"
    }
}
@Mixin(Helper)
class Target {
    def function() {
        System.out.println 'target#function-invoke'
    }
}

new Target().help()
//输出:help
class PersonA{ }
PersonA.mixin Helper
new PersonA().help()
//输出:help
复制代码

9.3.MOP方法合成

方法合成:想在调用时动态地确定方法的行为。Groovy的invokeMethod()、methodMissing()和GroovyInterceptable对于方法合成非常有用。

9.3.1.使用methodMissing()合成方法。
class Person {
    def work() { 'working...' }

    def methodMissing(String name, args) {
        System.out.println "methodMissing $name,$args"
        if(name == 'play'){
            "invoke method $name,$args"
        }else{
            throw new MissingClassException(name,Persion.class,args)
        }
    }
}

println new Person().play()
//输出: methodMissing play,[]
		invoke method play,[]
复制代码
9.3.2.使用ExpandoMetaClass合成方法。
Person.metaClass.methodMissing = { String name,args->
        System.out.println "methodMissing $name,$args"
        if(name == 'play'){
            "invoke method $name,$args"
        }else{
            throw new MissingClassException(name,Persion.class,args)
        }
    }

    println new Person().play()
    //输出: methodMissing play,[]
		invoke method play,[]
复制代码

总结

到此,我们学习到了变量,字符串,集合(List、Map、Range),闭包,动态特性、元编程(方法拦截、合成、注入),后续学习Gradle时会使用到这些技巧,故基础要打好。

文章分类
Android
文章标签