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.rhkb.cn/news/269527.html

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

相关文章

2024.02.29作业

1. TCP模型 server #include "test.h"#define SER_IP "192.168.191.128" #define SER_PORT 9999int main(int argc, char const *argv[]) {int sfd -1;sfd socket(AF_INET, SOCK_STREAM, 0);if (-1 sfd){perror("socket error");return -1;…

Java生成 word报告

Java生成 word报告 一、方案比较二、Apache POI 生成三、FreeMarker 生成 在网上找了好多天将数据库信息导出到 word 中的解决方案,现在将这几天的总结分享一下。总的来说,Java 导出 word 大致有 5 种。 一、方案比较 1. Jacob Jacob 是 Java-COM Bri…

ECMAScript6

课程链接 目录 相关介绍什么是ECMA什么是ECMAScript为什么学习ES6 letconst变量解构赋值模板字符串对象简化写法箭头函数函数参数的默认值rest参数扩展运算符Symbol迭代器生成器函数与调用Promise介绍与基本用法Promise封装读取文件Promise.prototype...then方法Promise.catch…

光影交织:汽车穿越隧道的视觉盛宴

在繁忙的城市中,隧道成为了连接两端的重要通道。而对于汽车来说,穿越隧道不仅是一次简单的空间转移,更是一场融合了视觉、技术与安全的独特体验。 当汽车缓缓驶入隧道,外界的光线逐渐减弱,隧道内部的光线开始发挥作用。…

向量数据库Chroma教程

引言 随着大模型的崛起,数据的海洋愈发浩渺无垠。受限于token的数量,无数的开发者们如同勇敢的航海家,开始在茫茫数据之海中探寻新的路径。他们选择了将浩如烟海的知识、新闻、文献、语料等,通过嵌入算法(embedding)的神秘力量,转化为向量数据,存储在神秘的Chroma向量…

JavaScript基础知识(三)

JavaScript基础知识(三) 一、事件1. 事件绑定2.事件流2.1 事件捕获与事件冒泡 3.事件对象扩展3.1 阻止事件冒泡3.2 事件默认行为 4.事件委托5.事件类型5.1 鼠标事件5.2 键盘事件5.3 触屏事件 二、计时器方法1. setInterval 与 clearInterval2. setTimeou…

【Unity】使用ScriptableObject存储数据

1.为什么要用ScriptableObject? 在游戏开发中,有大量的配置数据需要存储,这个时候就需要ScriptableObject来存储数据了。 很多人会说我可以用json、xml、txt,excel等等 但是你们有没有想过,假设你使用的是json&#x…

LeetCode148.排序链表

题目 给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。 示例 输入:head [4,2,1,3] 输出:[1,2,3,4] 输入:head [-1,5,3,4,0] 输出:[-1,0,3,4,5] 输入:head [] 输出:[] 思路…

通过GitHub探索Python爬虫技术

1.检索爬取内容案例。 2.找到最近更新的。(最新一般都可以直接运行) 3.选择适合自己的项目,目前测试下面画红圈的是可行的。 4.方便大家查看就把代码粘贴出来了。 #图中画圈一代码 import requests import os import rewhile True:music_id input("请输入歌曲…

openGauss学习笔记-236 openGauss性能调优-SQL调优-Query执行流程

文章目录 openGauss学习笔记-236 openGauss性能调优-SQL调优-Query执行流程236.1 Query执行流程236.1.1 调优手段之统计信息236.1.2 调优手段之GUC参数236.1.3 调优手段之底层存储236.1.4 调优手段之SQL重写 openGauss学习笔记-236 openGauss性能调优-SQL调优-Query执行流程 S…

Java 解决异步 @Async 失效问题

1.问题描述 使用Async进行异步处理时,异步没有生效 2.原因分析 经过排查后发现是因为使用Async的方法没有跨2个Service导致的 错误示例 控制器接口 > 直接调用 custAdminService.importCBuy() 3.解决方案 Controller接口不变,多添加一层Service&a…

【python开发】网络编程(上)

这里写目录标题 一、必备基础(一)网络架构1、交换机2、路由器3、三层交换机4、小型企业基础网络架构5、家庭网络架构6、互联网 (二)网络核心词汇1、子网掩码和IP2、DHCP3、内网和公网IP4、云服务器5、端口6、域名 二、网络编程案例…

大数据分析课----实时更新

1:Anaconda3 windows 安装 : 去官网下载; 然后安装好直接点next 点 i agree; 自己的电脑选第一个;学校的话选All Users ; 选择自己存放的目录; 选前三个; 安装即可; 一直…

UE4 Niagara 关卡3.4官方案例解析

Texture sampling is only supported on the GPU at the moment.(纹理采样目前仅在GPU上受支持) 效果:textures can be referenced within GPU particle systems。this demo maps a texture to a grid of particles(纹理可以在GPU粒子系统中被引用这个演…

牛客网C++专项题目整理(1)

1. 若有定义语句:char s[3][10],(*k)[3],*p;则以下赋值语句错误的是 1.p s; 2.p k; 3.p s[0]; 4.k s; 答案:124 char s[3][10] s 是数组指针,类型为char (*)[3],所指向的每个数组长度为10; char (*k)[3] k是一个数组指针&a…

【C++庖丁解牛】初始化列表 | Static对象 | 友元函数

📙 作者简介 :RO-BERRY 📗 学习方向:致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持 目录 1. 再谈构造函数1.1 …

小程序常用样式和组件

常用样式和组件 1. 组件和样式介绍 在开 Web 网站的时候: 页面的结构由 HTML 进行编写,例如:经常会用到 div、p、 span、img、a 等标签 页面的样式由 CSS 进行编写,例如:经常会采用 .class 、#id 、element 等选择器…

【计算机网络】TCP 如何实现可靠传输

TCP通过三次握手建立连接,四次挥手释放连接,确保连接建立和连接释放的可靠。 序列号、检验和、确认应答信号、重发机制、连接管理、窗口控制、流量控制、拥塞控制 标准回答 可靠传输就是通过TCP连接传送的数据是没有差错、不会丢失、不重复并且按序到达的…

React之数据绑定以及表单处理

一、表单元素 像<input>、<textarea>、<option>这样的表单元素不同于其他元素&#xff0c;因为他们可以通过用户交互发生变化。这些元素提供的界面使响应用户交互的表单数据处理更加容易 交互属性&#xff0c;用户对一下元素交互时通过onChange回调函数来监听…

【李沐论文精读】GAN精读

论文&#xff1a;Generative adversarial nets 参考&#xff1a;GAN论文逐段精读、生成对抗网络、李沐视频精读系列 一、介绍 什么是GAN? GAN(Generative adversarial network&#xff0c;生成对抗网络&#xff09;&#xff0c;它由生成器G&#xff08;Generator Neural Netwo…