Android Gradle开发与应用 (三) : Groovy语法概念与闭包

1. Groovy介绍

Groovy是一种基于Java平台的动态编程语言,与Java是完全兼容,除此之外有很多的语法糖来方便我们开发。Groovy代码能够直接运行在Java虚拟机(JVM)上,也可以被编译成Java字节码文件。

以下是Groovy的一些特性:

  • 简洁Groovy的语法比Java更加简洁,可以用更少的代码完成同样的功能。
  • 动态语言Groovy是一种动态语言,支持动态类型和动态方法调用等特性,这意味着你可以在编写代码时不必指定变量的类型。
  • 完全兼容JavaGroovy可以无缝使用Java的所有类库,也可以直接在Groovy代码中使用Java代码。

在这里插入图片描述

2. Groovy运行机制

Groovy是一种基于Java虚拟机(JVM)的面向对象编程语言,其运行机制主要包括以下几个方面:

  • 解析阶段:Groovy代码首先会被Groovy编译器解析为一个抽象语法树(AST)。AST是源代码的图形化表示,它以树状的形式描绘出源代码的结构,使编译器能够更好地理解和处理代码。

  • 编译阶段:在AST生成后,Groovy编译器会将它转换为Java字节码。这是因为Groovy是一种运行在JVM上的语言,必须将源代码转换为Java字节码,才能被JVM执行。

  • 运行阶段:生成的Java字节码最后会被JVM加载并执行。在这个过程中,如果Groovy代码中包含了动态类型,那么Groovy会在运行时进行类型检查和方法调用的解析。

  • 动态语言的特性:作为一种动态语言,Groovy的一大特性就是它的动态性。它支持动态方法调用,即在运行时解析方法调用,而不是在编译时。这使得Groovy在处理一些特定问题时更加灵活,例如处理JSON和XML等数据格式。

    • 可以想象成纯反射的调用,加上元编程的特性,使Groovy可以在运行时解析方法调用
    • 除非加上@CompileStatic会按照Java的方式静态编译,否则都是动态编译的
  • 元编程:Groovy还支持元编程,它允许开发者在运行时修改类的结构或行为。这使得Groovy可以实现一些强大的功能,例如创建DSL(领域特定语言)、添加或修改类的方法等。

  • 脚本执行:Groovy还可以作为脚本语言使用,即不需要进行编译,直接运行Groovy代码。在脚本模式下,Groovy会使用一个特殊的类加载器来解析和执行代码。

Groovy的运行机制深度整合了编译型语言和解释型语言的优势,既拥有编译型语言的性能优势,又保留了解释型语言的灵活性和便利性。

3. Groovy DSL

本身Groovy DSL的目标就是成为一个通用的DSL语言,所以在Groovy中,方法调用可以不写括号

比如 :

  • turn(left).then(right)可以简写为turn left then right
  • take(2.pills).of(chloroquinine).after(6.hours)可以简写为take 2.pills of chloroquinine after 6.hours
  • paint(wall).with(red, green).and(yellow)可以简写为paint wall with red, green and yellow
  • check(that: margarita).tastes(good)可以简写为check that: margarita tastes good
  • given({}).when({}).then({})可以简写为given { } when { } then { }

具体详见 Groovy DSL

3.1 Groovy DSL 示例一

比如我们在Android项目中经常可以看到这样一行代码

apply plugin: MyPlugin

这行代码等价于

apply([plugin : MyPlugin])

当方法的参数是一个map的时候,可以将方括号[]去掉

apply(plugin: MyPlugin)

当不引起歧义的时候,可以把圆括号去掉,从而得到了我们经常看到的这行代码

apply plugin : MyPlugin

3.2 Groovy DSL 示例二

在新版的Gradle中,默认情况下,已经不使用apply plugin了,而是使用plugins{}来引入插件了。

plugins {id 'com.android.application' version '8.1.3' apply false
}

本质是有一个plugins的方法,调用了一个id 'com.android.application' version '8.1.3' apply false的闭包

plugins({id('com.android.application').version('8.1.3').apply(false)
})

4. 闭包

4.1 最简单的闭包

先来看一个最简单的闭包

//声明一个闭包
def closure = {println "hello world!"//return 1
}//可以直接调用它,因为它就是一个函数
closure()
//等同于上面这行
closure.call()

4.2 带参数的闭包

带参数的闭包只需要传入需要的参数,声明闭包的时候,指明这个参数(比如param1)就好了

def closure = { param1 ->println("running start...:" + param1)println("running end...")
}//进行调用,并传参
closure("heiko")
//等同于上面这行
closure.call("qwerty")

打印的日志

running start...:heiko
running end...
running start...:qwerty
running end...

4.3 闭包在实际开发中的应用

4.3.1 无参数

一般在实际开发中,闭包是作为传参传入的,通过closure.call()进行回调

def closure(Closure closure){println("running start...")//closure() 这种调用方式也可以closure.call()println("running end...")
}

然后在调用方法的时候,就可以很方便的通过闭包{}进行调用了

closure {println("running........")
}

打印的日志如下

running start...
running........
running end...
4.3.2 有参数的情况

闭包有参数的情况,那么通过closure.call()传入了两个参数1015

def calc(Closure closure) {//closure(10,15) 这种调用方式也可以def result = closure.call(10, 15)println("result:" + result)
}

那么在调用方法的时候,闭包可以声明v1,v2这两个参数,然后就可以直接使用了

calc { v1, v2 ->println("v1:" + v1 + " v2:" + v2)v1 + v2
}

打印的日志如下

v1:10 v2:15
result:25
4.3.3 调用闭包的时候传参

调用方法的时候,我们可以传参,然后还可以将这个参数,回调给闭包closure.call(num1, num2)

def calc2(num1, num2, Closure closure) {//closure(num1,num2) 这种调用方式也可以def result = closure.call(num1, num2)println("result:" + result)
}

调用方法的时候,就是在()里多传入两个参数就好了

calc2(6, 7) { v1, v2 ->println("v1:" + v1 + " v2:" + v2)v1 + v2
}

打印日志如下

v1:6 v2:7
result:13

4.4 闭包{}是怎么出现的

4.4.1 最初的闭包
def calc3(num1, num2, Closure closure) {//closure(num1,num2) 这种调用方式也可以def result = closure.call(num1, num2)println("result:" + result)
}
4.4.2 调用方法

闭包作为方法的最后一个参数的时候,可以写在方法外面

calc3(1, 2) { v1, v2 ->println("v1:" + v1 + " v2:" + v2)v1 + v2
}
4.4.3 方法没有 其他参数的情况

如果方法没有其他参数的话,调用的时候是(),闭包{}()外面

def calc3(Closure closure) {def result = closure.call(num1, num2)println("result:" + result)
}calc3() { v1, v2 ->println("v1:" + v1 + " v2:" + v2)v1 + v2
}
4.4.4 省略大括号

方法调用的时候,在不引起歧义的情况下,大括号()也可以省略,这样就成为我们最终看到的闭包的样子了。

def calc3(Closure closure) {def result = closure.call(1, 2)println("result:" + result)
}calc3 { v1, v2 ->println("v1:" + v1 + " v2:" + v2)v1 + v2
}

5. 写一个自己的android闭包

Android项目,我们平时最常见的就是android这个闭包了,那么我们能不能自己写一个android闭包呢

android {namespace 'com.heiko.mytest'compileSdk 34defaultConfig {applicationId "com.heiko.mytest"minSdk 24targetSdk 34}
}

5.1 声明MyAndroidBean类

声明MyAndroidBean类,用来定义需要传递的参数

class MyAndroidBean {public String namespacepublic Integer compileSdk
}

5.2 声明函数 : myandroid

声明函数myandroid,传参为一个闭包closure,然后调用project.configure(myAndroidBean, closure)使闭包转化为MyAndroidBean,然后就可以调用myAndroidBean的属性了。

def myandroid(Closure closure) {MyAndroidBean myAndroidBean = new MyAndroidBean()project.configure(myAndroidBean, closure)println(myAndroidBean.namespace)println(myAndroidBean.compileSdk)
}

5.3 调用myandroid

接着写上这些代码,来调用myandroid,并配置了namespacecompileSdk的值

myandroid {namespace = "com.heiko.mm"compileSdk = 31
}

5.4 Sync下项目

然后我们Sync下项目,可以发现打印出了如下日志

myandroid {namespace = "com.heiko.mm"compileSdk = 31
}

5.5 声明MyDefaultConfig类

声明MyDefaultConfig类,用来定义mydefaultConfig闭包内的参数

class MyDefaultConfig {public String applicationIdpublic int minSdkpublic int targetSdk
}

5.6 声明函数 : mydefaultConfig

声明函数mydefaultConfig,传参为一个闭包closure,然后调用closure.delegate = configclosure.delegate = defaultConfig这行代码的作用是将闭包的委托对象设置为defaultConfig实例。这意味着在闭包内部,当你尝试访问或设置一个属性(如applicationId、minSdk或targetSdk)时,实际上是在defaultConfig对象上执行这些操作。

class MyAndroidBean {public String namespacepublic Integer compileSdkpublic MyDefaultConfig defaultConfigdef mydefaultConfig(Closure closure) {MyDefaultConfig config = new MyDefaultConfig()closure.delegate = configclosure.call()defaultConfig = config}
}def myandroid(Closure closure) { // 添加project参数MyAndroidBean myAndroidBean = new MyAndroidBean()closure.delegate = myAndroidBeanclosure.call()println("namespace:" + myAndroidBean.namespace)println "compileSdk:" + (myAndroidBean.compileSdk)println "applicationId:" + (myAndroidBean.defaultConfig.applicationId)println "minSdk:" + (myAndroidBean.defaultConfig.minSdk)println "targetSdk:" + (myAndroidBean.defaultConfig.targetSdk)
}

在Groovy中,闭包(Closure)是一种可以引用和使用其周围环境中的变量的代码块。闭包有三种重要的属性:delegate、owner和this。
delegate属性是执行闭包时用于解析方法调用和属性引用的对象。也就是说,当你在闭包内部调用一个方法或引用一个属性,Groovy会首先在delegate对象上查找这个方法或属性。如果在delegate对象上找不到,它将在owner和this对象上查找。
默认情况下,delegate对象是owner对象,但你可以自由地改变它。当你设置了一个新的delegate,你可以在闭包中引用和操作这个新对象的方法和属性,就像它们是在闭包内部定义的一样,这个特性使得你可以在闭包中使用DSL样式的代码。

5.7 调用mydefualtConfig

这个时候就可以去调用mydefaultConfig方法了,并可以对applicationId、minSdk、targetSdk属性进行配置。

myandroid {namespace = "com.heiko.mm"compileSdk = 31mydefaultConfig {applicationId = "com.heiko.mm"minSdk = 21targetSdk = 31}
}

最后Sync下项目,可以看到打印日志如下

namespace:com.heiko.mm
compileSdk:31
applicationId:com.heiko.mm
minSdk:21
targetSdk:31

6. Gradle系列文章

Android Gradle 开发与应用 (一) : Gradle基础-氦客-CSDN博客
Android Gradle开发与应用 (二) : Groovy基础语法-氦客-CSDN博客
Android Gradle开发与应用 (三) : Groovy语法概念与闭包-氦客-CSDN博客
Android Gradle开发与应用 (四) : Gradle构建与生命周期-氦客-CSDN博客
基于Gradle 8.2,创建Gradle插件-氦客-CSDN博客
Android Gradle插件开发_实现自动复制文件插件-氦客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/724809.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图像处理与视觉感知---期末复习重点(1)

文章目录 一、概述二、图像处理基础2.1 视觉感知要素2.2 像素间的一些基本关系2.2.1 相邻像素2.2.2 连通性2.2.3 距离度量 2.3 基本坐标变换2.4 空间变换与灰度值 一、概述 1. 图像的概念及分类。  图像是用各种观测系统以不同形式和手段观测客观世界而获得的、可以直接或间接…

nodejs版本管理工具nvm安装和环境变量配置

1、下载nvm.exe https://github.com/coreybutler/nvm-windows/releases2、安装 1.在D盘根目录新建一个dev文件夹,在dev里面再新建一个nodejs。 2.双击下载好的nvm.exe 修改文件路径,且路径中不能有中文 3.安装完成后在D:\dev\nvm打开settings.txt&…

kerberos学习系列一:原理

1、简介 Kerberos 一词来源于古希腊神话中的 Cerberus —— 守护地狱之门的三头犬。 Kerberos 是一种基于加密 Ticket 的身份认证协议。Kerberos 主要由三个部分组成:Key Distribution Center (即KDC)、Client 和 Service。 优势: 密码无需进行网络传…

Docker数据卷篇

1. 数据卷(容器数据管理) 引言:在之前的nginx案例中,修改nginx的html页面时,需要进入nginx内部。并且因为没有编辑器,修改文件也很麻烦。 这就是因为容器与数据(容器内文件)耦合带…

Scrapy与分布式开发(3):Scrapy核心组件与运行机制

Scrapy核心组件与运行机制 引言 这一章开始讲解Scrapy核心组件的功能与作用,通过流程图了解整体的运行机制,然后了解它的安装与项目创建,为后续实战做好准备。 Scrapy定义 Scrapy是一个为了爬取网站数据、提取结构性数据而编写的应用框架…

Claude3荣登榜首,亚马逊云科技为您提供先行体验!

Claude3荣登榜首,亚马逊云科技为您提供先行体验! 个人简介前言抢先体验关于Amazon BedrockAmazon Bedrock 的功能 Claude3体验教程登录Amazon Bedrock试用体验管理权限详细操作步骤1.提交应用场景详细信息2.请求模型的访问权限3.请求成功,开始…

Mybatis实现分页查询数据(代码实操讲解)

在MyBatis中实现分页查询的常见方式有两种:使用MyBatis内置的分页插件如PageHelper,或者手动编写分页的SQL语句。下面我将为你提供两种方式的示例代码。 使用PageHelper分页插件 首先,确保你的项目中已经添加了PageHelper的依赖。在Maven项…

overleaf latex 笔记

overleaf: www.overleaf.com 导入.tex文件 1.代码空一行,代表文字另起一段 2. 1 2 3 排序 \begin{enumerate} \item \item \item \end{enumerate} 3.插入图片 上传图片并命名 \usepackage{float}导包\begin{figure}[H]:表示将图…

【网络安全】漏洞挖掘入门教程(非常详细),小白是如何挖漏洞(技巧篇)0基础入门到精通!

温馨提示: 初学者最好不要上手就去搞漏洞挖掘,因为漏洞挖掘需要很多的系统基础知识和一些理论知识做铺垫,而且难度较大…… 较合理的途径应该从漏洞利用入手,不妨分析一些公开的CVE漏洞。很多漏洞都有比较好的资料,分…

【四】【SQL Server】如何运用SQL Server中查询设计器通关数据库期末查询大题

数据库学生选择1122 数据库展示 course表展示 SC表展示 student表展示 数据库学生选课1122_3 第十一题 第十二题 第十三题 第十四题 第十五题 数据库学生选课1122_4 第十六题 第十七题 第十八题 第十九题 第二十题 数据库学生选课1122_5 第二十一题 第二十二题 结尾 最后&…

保留数据的重装系统教程!(win10系统)

上车警告!!! 本教程无需思考,跟着操作一步一步来就能完成系统的重装。原理是将C盘系统重装,其他盘符数据保存。适用于系统盘重装数据或更改系统版本。 重要提示!!! C盘有重要学习资…

Long-term Correlation Tracking LCT目标跟踪算法原理详解(个人学习笔记)

目录 1. 算法总览2. 算法详解2.1. 基础相关滤波跟踪2.2. 各模块详解2.2.1. 相关跟踪2.2.2. 在线检测器 3. 算法实现3.1. 算法步骤3.2. 实现细节 4. 相关讨论&总结 1. 算法总览 LCT的总体流程如上图所示,其思想为:将长时跟踪(long-term tr…

第五节 JDBC驱动程序类型

JDBC驱动程序是什么? JDBC驱动程序在JDBC API中实现定义的接口,用于与数据库服务器进行交互。 例如,使用JDBC驱动程序,可以通过发送SQL或数据库命令,然后使用Java接收结果来打开数据库连接并与数据库进行交互。 JDK…

18.四数之和

题目:给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复)&#x…

每日一题 — 有效三角形的个数

611. 有效三角形的个数 - 力扣(LeetCode) 思路: 双指针思想,先将数据排序 然后先固定最大的数Max,也就是数组最后的数再定义一左一右两个下标 left 、 right,当这个值相加大于最大的数,那么他两…

Pytorch学习 day05(RandomCrop、Transforms工具使用总结)

RandomCrop 将PIL或Tensor格式的输入图片,随机裁剪指定尺寸的部分输入尺寸可以为序列或单个整形数字代码如下: from PIL import Image from torchvision import transforms from torch.utils.tensorboard import SummaryWriterimg Image.open("i…

python高级之元类

python高级之元类 一、Type创建类1、传统方式创建类2、非传统方式 二、元类三、总结 一、Type创建类 class A(object):def __init__(self, name):self.name namedef __new__(cls, *args, **kwargs):data object.__new__(cls)return data根据类创建对象 objA(‘kobe’) 1、执…

外包干了2年,技术退步明显

先说一下自己的情况,研究生,19年进入广州某软件公司,干了接近4年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&#xf…

18个惊艳的可视化大屏(第18辑):数字资产场景

hello,我是贝格前端工场,本次分享可视化大屏在数字资产领域的应用,喜欢文章的别忘点赞关注,文章底部也有其他行业的案例,有需求您说话(可私信)。 数字资产可视化大屏可以应用于各种场景&#x…

Vue.js+SpringBoot开发森林火灾预警系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 系统基础模块2.3 烟雾传感器模块2.4 温度传感器模块2.5 历史记录模块2.6 园区数据模块 三、系统设计3.1 用例设计3.1.1 森林园区基础系统用例设计3.1.2 森林预警数据用例设计 3.2 数据库设计3.2.1 烟雾…