2020 Hello Groovy

Hello Groovy

相信做过Android开发的小伙伴都有过如下的经历,这个.build是干什么的,打开看看,看过之后大概意思明白了,但是自己写又写不出来。另外报错的时候,一般就是按照现有的模板比较下,然后找找错,有不同的地方可能就是有错的地方。

总之大多数时候处在一种,我能看明白,但是写不出来的尴尬境地。另外我也不知道他是什么原理,我只知道打包的时候需要用到这个文件,所以在打包阶段的需求写在这个文件里面就可以,如签名混淆这些。

不过,随着我们对打包阶段的需求逐渐升级,比如引入组件化后需要控制单Module调试,以及组件化后的依赖控制,再往后,为了提升组件化效率我们需要开始开发一些基于编译时注解的Gradle插件,此时如果我们还是做只基于创建项目时的模板知识显然已经不够了。尤其是我们开发Gradle插件时,是必须学习一些Groovy语言的,另外当我们学习了Groovy语言再回头看.gradle文件时,就会有一种原来如此的感觉。

Groovy简介

Groovy是一种基于JVM虚拟机的动态语言,他的语法和Java非常类似,所以我们会有一种看的懂,写不出的感觉。Groovy完全兼容Java,又在此基础上增加了很多动态类型和灵活的特性,比如支持闭包。是一门非常灵活的脚本语言。另外在我们的项目中没有Gradle的.build文件都是一个Groovy脚本文件,你可以在里面写任何符合Groovy语法的代码。Groovy内部会把代码编译为Java class文件然后启动虚拟机来执行。

在Groovy中,一切都是对象,没有原子类型。

在Groovy中,所有的操作符都是方法调用。

一、声明变量

Groovy支持静态和动态的声明变量。静态声明可以简单的理解为明确了声明对象是哪个类;动态声明可以理解为,声明的时候不确定是那个类,实例化的时候再确定。

声明动态变量:

def params

声明静态变量:

string params="Hello World!"

Groovy可以直接使用Java中的类作为声明。

注:Groovy的语句末尾是可以不写分号的!建议大家在编写时尽量不写通过换行来区分语句。

二、字符串

在Groovy中单引号和双引号都可以定义字符串常量主要的区别是,单引号常量是不对内容进行处理的,是纯粹的字符串常量,而双引号是可以对内容进行处理的。
举个🌰:

task printStringClass<<{
	def str1='单引号';
	def str2="双引号";
	
	println "单引号定义的字符串类型:"+str1.getClass().name;
	println "双引号定义的字符串类型:"+str2.getClass().name;
}

输出的结果为:

单引号定义的字符串类型:java.lang.String
双引号定义的字符串类型:java.lang.String

由此可知在Groovy中单引号和双引号创建的都是String类型的对象。
下面再举个🌰,解释下处理字符串这个概念:

task printStringVar<<{
	def params="Hello World!";
	
	println '单引号的变量处理${params}'
	println "双引号的变量处理${params}"	
}

输出结果为:

单引号的变量处理${params}
双引号的变量处理Hello World!

这里要说明的是在Groovy中使用引用值时,需要使用${}的形式来引入变量。这样对比java的+号形式显得更舒服一些。

三、集合

由于我们可以在Groovy中引用Java的类,那么此时这些集合的使用和Java的集合是一样的,这里我们说下Groovy中的结合List,Map。其他的如Set和Queue可以自行查阅。

1.List

先来看下如何创建一个List:

task demoList1<<{
	def list=[]
	def numberList=[1,2,3,4];
	println numberlist.getClass.name;
}

上面的语句第一行声明可一个空的List第二个声明了一个包含元素的List。输出numberList的类型为ArrayList。

接着我们看下如何拿到一个元素,以及Groovy在获取元素时的特性。

task demoList2<<{
	def numberList=[1,2,3,456];
	println numberlist[1];
	println numberlist[-1];
	println numberlist[-2];
	println numberlist[1..3];
}

Groovy提供了可以像数组一样的方式来获取,集合中的元素。细心的小伙伴可能已经发现了-1和-2这两个索引位置,放心在Groovy中这样写并不会越界,在Groovy中-1和-2代表着集合中倒数第一个元素和倒数第二个元素。至于"1…3"这种写法,熟悉脚本语言或者看过kotlin的小伙伴也不会陌生,这是一个集合索引,代表取出集合中第一个到第三个元素。

最后在说一下遍历:

task demoList2<<{
	def numberList=[1,2,3,456];
	 numberList.each({
		println it
	})
}

这种写法相信熟悉脚本语言的同学一样不陌生啦。当然我们也可以使用Java中for的形式去遍历。

2.Map

Map的概念就是键值对集合,存储时需要给value一个key进行指向,取出时使用key取出。
先看下Groovy中如何定义一个Map:

task demoMap<<{
	def map1=[:]
	def map2=['width':1024,'height':1024]
	println map2.getClass().name
}

这里面第一行是定义了一个空的Map,第二行是定义了一个有内容的Map。

接下里说一下如何获取Map中的元素:

task demoMap2<<{
	def map1=['width':1024,'height':1024]
	println map1['height']
	println map1.width
}

以上两种方式都可以获取map中的元素。

最后还是说一下Map的迭代:

task demoMap3<<{
	def map1=['width':1024,'height':1024]
	map1.each{
		println "Key:${it.key},Value:${it.value}"
	}
}

对于集合来说以上的例子是远远不够的这里只是简单的介绍一下,使看惯了Java代码的我们在看Groovy语言时不会感到陌生。如果对于Groovy的集合想更加深入的了解,可以查看文尾的Groovy中文官方文档。

四、方法

1.Groovy的方法特性

在我们使用Java的语言时,调用一个方法一般是:

method(params1,params2);

不过在Groovy中我们在调用方法时括号是可以省略的如:

method param1,param2

当我们方法存在返回值的时候,在Java中我们往往需要写一个return来返回方法结果。而在Groovy中这个 return也是可以省略的。下面看下如何声明这样的方法:

def method(int a,int b){
	if(a>b){
	  a
	}else{
	  b
	}
}

这里我们需要关注的点是,第一方法声明时,有动态变量标示,可以自行推断返回类型。而我们在处理玩判断逻辑的时候不用声明return了只需要把返回内容写上即可。

2.Groovy中传递代码块

这里面有点像Java中的匿名内部类,即传入的是一段可以执行的逻辑。不过在Groovy中这种传递写法更加简单高效。
这里我们来看下我们之前写的一个例子:

task demoList2<<{
	def numberList=[1,2,3,456];
	 numberList.each({
		println it
	})
}

我们在这个each中传递的就是代码块变量。当然这种写法还是偏向于Java风格的。那么我们现在按照Groovy的形式改变一下写法:

//如果最后一个参数是代码块(闭包类型的参数)是可以放到方法外面的
numberList.each(){
		println it
}
//再结合Groovy中方法是可以不写括号的
numberList.each {
		println it
}

五、JavaBean

相信做Java开发的小伙伴门,对JavaBean这个概念并不陌生。JavaBean一般是对一个对象属性描述的集合,包含着这个对象属性的get/set方法。虽然编译器可以快速的为我们生成get/set方法,不过在字段变多的时候,JavaBean对象往往显得臃肿且不易直接的查找有那些属性,以及是否对这些属性做了特殊处理。

那么在Groovy中就真对这些问题做了很多的优化:

task demoJavaBean<<{
	def person=new Person()
	println "person.name=${person.name}";
	person.name="Hello World!"
	println "person.name=${person.name}";
}

class Person{
	private String name;
}

调试上面的程序的时候我们,第一次输出name的值时我们看到,返回的是null。赋值后我们输出的就是"Hello World!"了,此时我们发现虽然声明了私有,但是我们已然可以方便的访问和操作类的属性。

上面的操作虽然解决了get/set的问题,但是有的时候我们不希望别人可以对属性写操作,或者在获取属性值时,希望属性是经过处理的。那么显然上面的形式是不满足的。下面给大家展示的方式解释解决这类问题。

task demoJavaBean<<{
	def person=new Person()
	println "person.name=${person.name}";
	person.name="Hello World!"
	println "person.name=${person.name}";
	println "person.age=${person.age}";
}

class Person{
	private String name;
	
	public int getAge(){
	   //不需要写return,Groovy可以直接返回
	   12
	}
}

此时我们声明了一个getAge()方法,这样书写的特点是,我们只能取值,不能赋值,因为未开放set方法。

六、闭包

Groovy中闭包是非常重要的一个特性,由于闭包的存在,可以大量的节省代码量,只需要让需要复用的代码变成闭包,然后进行闭包传递就可以了。
注闭包对象的类为:Closure

1.单参数或无参数闭包

task demoClosure<<{
	customEach{
		println it
	}
}

```java
def customeEach(Closure closure)
{
	for(int i in 1..10)
	{
		closure.call(i);
	}
}

上面的代码对于刚接触闭包这个概念的小伙伴可能有些摸不到头脑。
我们先确定闭包的概念:闭包其实就是一段代码块,Groovy可以传递代码块。
接下来我们来解析一下上面的代码:

//声明方法,方法传递的参数是闭包对象
def customeEach(Closure closure)
{
    //让i在1-10这个范围内遍历
	for(int i in 1..10)
	{
		//闭包对象,需要有一个整形的输入参数,输入参数并执行
		closure.call(i);
	}
}

这是方法代码的解析,下面看执行代码

task demoClosure<<{
	//调用方法,此方法的最后一个参数是闭包参数,可以将代码块写到括号外面;Groovy可以不写括号省略括号
	customEach
	//闭包(代码块参数),其执行逻辑是输出一个参数,一个参数可以省略
	{
		println it
	}
}

整个来看就是依次输出1-10十个数字。

2.多参数闭包

上面给出的是向闭包传递一个参数的情形,此时我们可以直接使用it即可。显然传递多个参数时it就不能胜任了,下面我们看下如何向闭包传递多个参数。

task demoClosure2<<{
	//调用方法,省略括号,直接声明闭包
	customEach {
		//显示声明两个参数,并用-> 将参数和主体分开
		String key,int value->
		//拿到参数后的真正执行逻辑
		println "key=${key},value=${value}";
	}
}

//声明一个闭包参数的方法
def customEach(Closure closure){
	//方法内有一个map,声明map的属性值
	def map1=["param1":100,"param2":200]
	//遍历map,map.each的最后一个参数是闭包参数,省略括号,直接声明代码段。
	map1.each{
		//闭包的call方法传递的是(Object... args)即可以传入任意个对象,此时我们声明的是需要传入两个对象
		closure.call(it.key,it.value);
	}
}

多个参数时,我们就需要在传递时使用显示声明的形式声明需要传递的变量,然后用->将闭包的参数和主题区分开来。上面代码的理解还是从方法开始,从下往上看。

3.闭包中的委托

要理解闭包,那么就必须要了解闭包中的三个属性,该属性仅在闭包中可以使用!

this:该属性指向定义闭包的类的实例对象
owner:该属性和 this 类似,但是闭包中也可以定义闭包的,如果闭包 A 内定义了闭包 B,那么闭包 B 的 owner 指向的是其外部的闭包 A
delegate:该值初始化时是和 owner 相同的,但是该值可以通过接口将其它对象赋值给 delegate,来实现方法的委托功能,这正是 groovy 精彩之处!

class Enclosing {
    void run() {
    	//定义闭包对象 
        def whatIsThisObject = { getThisObject() }       
        assert whatIsThisObject() == this                   
        def whatIsThis = { this }                           
        assert whatIsThis() == this                         
    }
}
class EnclosedInInnerClass {
    class Inner {
        Closure cl = { this }                               
    }
    void run() {
        def inner = new Inner()
        assert inner.cl() == inner                          
    }
}
class NestedClosures {
    void run() {
        def nestedClosures = {
            def cl = { this }                               
            cl()
        }
        assert nestedClosures() == this                     
    }

以上的代码主要阐述了,闭包处于不同的类型形式时,其所有者是谁,以及调用所有这方法时,调用的是哪一级的所有者。

在闭包内引用的变量和方法都会绑定到 this,其负责处理任何方法调用,以及对任何属性的访问。如果 this 无法处理,则转向 owner,最后在转给 delegate,如果再找不到,那么就会抛出异常。这就是 groovy 提供的默认的策略 this -> owner -> delegate

当然该顺序也是可以改变的,可以通过闭包的 resolveStrategy 属性,指定不同的策略。

Closure.OWNER_FIRST 是默认策略。如果属性或者方法存在于 owner 内,那么他可以被 owner 调用,如果不存在,则会尝试在 delegate 类中查找
Closure.DELEGATE_FIRST 颠倒了默认逻辑:delegate 是第一优先级,其次才是 owner
Closure.OWNER_ONLY 将仅仅在 owner 查找需要的属性或者方法:delegate 会被忽略
Closure.DELEGATE_ONLY 将仅仅在 delegate 查找需要的属性或者方法:owner 会被忽略
Closure.TO_SELF 可以被用于当开发人员需要使用先进的元数据编程技术和希望实现一个自定义的选择策略时:这个选择将不是 owner 或者 delegate,而仅仅是 closure 类自己。当我们实现了自己的 Closure 子类时,他才是有意义的。

class Person {
    String name
    def pretty = { "My name is $name" }             
    String toString() {
        pretty()
    }
}
class Thing {
    String name                                     
}

def p = new Person(name: 'Sarah')
def t = new Thing(name: 'Teapot')

assert p.toString() == 'My name is Sarah'           
p.pretty.delegate = t                               
assert p.toString() == 'My name is Sarah'  

断言结果:第一次是true 第二次是false.

七、官方中文文档

目前的知识只是为了我们可以一定程度的看懂Gradle和开发Gradle插件。覆盖面较小,所以最后这是Groovy的官方开发文档:
http://jvm123.com/doc/groovy/index.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值