Groovy 学习手册(6)
9. 不可变特性
不可变特性和函数式编程在一起就像是花生酱和果酱在一起一样。虽然没有必要非要在一起使用,但他们相处得很好。
在纯正的函数式语言中,每个函数对本身之外没有影响,即没有副作用。这意味着每次调用函数时,返回相同输入的相同值。
为了适应这种行为,需要不可变的数据结构。不可变的数据结构不能直接更改,但每次操作都返回一个新的数据结构。
例如,Scala语言里的 Map 就是不可变的。
val map = Map("Smaug" -> "deadly")
val map2 = map + ("Norbert" -> "cute")
println(map2) // Map(Smaug -> deadly, Norbert -> cute)
所以,在上面的代码中,map是保持不变的。
每个语言里都会提供一个关键字来定义一个变量或值是不可变的,在 Java 中使用 final 关键字来表示,Groovy 里面也是一样。
public class Centaur {
final String name
public Centaur(name) {this.name=name}
}
Centaur c = new Centaur("Bane");
println(c.name) // Bane
c.name = "Firenze" //error
除此而外,Groovy 还提供了 @Immutable 注解来标注一个类为不可变类。它也提供了默认的构造方法,还有带有属性的构造方法,hashCode 方法,equals 方法和 toString 方法。下下面的例子。
import groovy.transform.Immutable
@Immutable
public class Dragon {
String name
int scales
}
Dragon smaug = new Dragon('Smaug', 499)
println smaug
// Output: Dragon(Smaug, 499)
10. Groovy Fluent GDK
在 Groovy 中,findAll和其他方法可以用在任何对象上,但尤其用在 List,Set,Range 上更加好用。除了findAll,collect,inject还有以下的方法都可以在 Groovy 中使用。
- each:根据给定的闭包条件遍历所有的值;
- eachWithIndex:在遍历值时带有两个参数,一是具体的值,二是值对应的索引;
- find:根据匹配的闭包条件找到满足条件的第一个元素;
- findIndexOf:根据匹配的闭包条件找到满足条件的第一个元素,并返回对应的索引。
例如,collect方法用在对 List 的元素操作上非常简单。
def list = ['foo','bar']
def newList = []
list.collect( newList ) { it.substring(1) }
println newList // [oo, ar]
在另一个例子里,
def dragons = [new Dragon('Smaug', 499), new Dragon('Norbert', 488)]
String longestName = dragons.
findAll { it.name != null }.
collect { it.name }.
inject("") { n1, n2 -> n1.length() > n2.length() ? n1 : n2 }
上面的代码首先查找所有不为 null 的名字,收集对象元素里的的名字属性,然后再使用聚合方法找到所有名字里最长的那个。
11. Groovy Curry
curry方法用来在给闭包里的参数预定义一个默认值。它可以接受多个参数,并且在你预想的情况下从左到右逐一替换。请看下面的代码:
def concat = { x, y -> return x + y }
// closure
def burn = concat.curry("burn")
def inate = concat.curry("inate")
因为你只提供了第一个参数,它们的闭包会准备已经给定的字符串,burn方法准备了“burn”字符串,而inate方法准备了“inate”字符串。
burn(" wood") // == burn wood
你还可以使用composition闭包应用于两个方法和一个输入参数。
def composition = { f, g, x -> return f(g(x)) }
def burninate = composition.curry(burn, inate)
def trogdor = burninate(' all the people')
println "Trogdor: ${trogdor}"
// Trogdor: burninate all the people
函数组合是函数编程中的一个重要思想。它允许你组合多个功能在一起创建复杂的算法。
12. 方法句柄
方法句柄允许你引用实际方法,就像它们是闭包一样。当你想使用现有的方法,而不是闭包,或者你只是想一个替代的闭包语法时,这是非常有用的。例如,给定一个方法:
def breathFire(name) { println "Burninating $name!" }
在定义了breathFire方法的类里面可以做如下操作:
['the country side', 'all the people'].each(this.&breathFire)
这样就会把breathFire方法作为一个闭包传递给 each 方法,然后会打印如下结果:
Burninating the country side
Burninating all the people
13. 尾递归
在 Groovy 1.8版本中,trampoline方法被用来引入到闭包中用于对尾递归的优化。这允许闭包被顺序调用而不是内存堆积,从而避免栈溢出和提高性能。
从Groovy 2.3 版本以后,可以使用trampoline方法来解决递归,甚至更好的办法是使用注解 @TailRecursive, 例如,
import groovy.transform.*
@TailRecursive
long totalPopulation(list, total = 0) {
if (list.size() == 0)
total
else
totalPopulation(list.tail(), total + list.first().population)
}
在 Groovy 的 List中,tail 方法会返回不包含第一个元素的 List,而first方法则返回 List 的第一个元素。直接上代码:
@Canonical class City {int population}
def cities = (10..1000).collect{new City(it)}
totalPopulation(cities)
// 500455
Groovy 学习手册(6)的更多相关文章
- Groovy 学习手册(7)
10. Groovy GPars GPars 一开始在 Groovy 中作为一个单独的项目,同时带来了很多并发的机制.它包含了很多并行的map/redue,Actors,以及其他很多并发的模块. 1. ...
- Groovy 学习手册(5)
8. 函数式编程 函数式编程(FP)是一种编程风格,侧重于函数和最小化状态的变化(使用不可变的数据结构).它更接近于用数学来表达解决方案,而不是循序渐进的操作. 在函数式编程里,其功能应该是" ...
- Groovy 学习手册(4)
6. 领域特定语言 Groovy 有许多特性,使它非常适合写DSL(领域特定语言).这些特性包活: 具有委托机制的闭包: 点号(.)和语句末尾的分号(;)是可选的: 运算符的重载(例如,加号,减号等) ...
- Groovy 学习手册(3)
五. Groovy 的设计模式 设计模式是一种非常好的方式,可以使你的代码变得实用,可读又具有扩展性.跟 Java 相比,在 Groovy 里使用设计模式使代码更加简洁和容易. 1. 策略模式 设想一 ...
- Groovy 学习手册(2)
二. 工具 1. 控制台 groovyConsole: Groovy 控制台是一个非常易于使用和简单的轻量级的编辑器.你可以在里面做很多事情. 在编辑器里面可以书写代码,Windows 下,按下Ctr ...
- Groovy 学习手册(1)
1. 需要安装的软件 Java / Groovy 对应 Java 和 Groovy,你需要安装以下软件: Java JDK,例如 JDK 8 IDE,例如 Eclipse,NetBeans 8 Gro ...
- Kotlin强化实战!这份学习手册让你的面试稳如泰山
一.引言 正如官网的slogan所描述:kotlin,是一门让程序员写代码时更有幸福的现代语言. 同时,也正如维基百科里介绍: JetBrains公司希望Kotlin能够推动IntelliJ IDEA ...
- Redis学习手册(目录)
为什么自己当初要选择Redis作为数据存储解决方案中的一员呢?现在能想到的原因主要有三.其一,Redis不仅性能高效,而且完全免费.其二,是基于C/C++开发的服务器,这里应该有一定的感情因素吧.最后 ...
- git学习手册
#git学习手册 git: Git是一个开源的分布式版本控制系统,可以有效.高速的处理从很小到非常大的项目版本管理.[2] Git 是 Linus Torvalds 为了帮助管理 Linux内核开发而 ...
随机推荐
- SpringMVC框架使用注解执行定时任务(转)
首先要配置我们的SpringMVC文件 xmlns 加下面的内容: xmlns:task="http://www.springframework.org/schema/task" ...
- 让你的ASP.NET虚拟主机也支持子网站
现在asp.net虚拟主机一般都可以绑定多个域名,但是通过这几个域名打开的页面都一样.如何使绑的这几个域名分别打开不通的页面(即实现子网站的功能)呢? 其实很简单,只需4个步骤: 1)给虚拟主机 ...
- 无法将 Ethernet0连接到虚拟网络 VMnet8 以及无法使用桥接的详细解决步骤
前言 首先我们需要明确如下表所示的对应关系: 网络类型 网络适配器名 Bridged VMnet0 NAT VMnet8 Host-only VMnet1 解决"无法将 ...
- JMX入门开发
什么是JMX?或者是JMX是做什么的?我的理解是:可以远程管理/编辑JAVA对象.如图: 上面的SchemaName属性就是可以动态修改的,那么是如何做到的哪?下面咱们逐步分析. 一.首先假设咱们有个 ...
- intellij idea 插件安装、卸载
windows 下 intellij idea 插件安装.卸载 安装(在线安装): 根据图一.图二所示(蓝色标记) 卸载: 根据图一所示(橙色标记) 启用.关闭插件: 根据图一所示(绿色标记) 安 ...
- Unix线程概念、控制原语、属性
线程: 线程基础概念: 线程在Linux中又称轻量级进程.而且它和进程都有PCB(进程控制块).可是差别是进程的虚拟地址空间是独享的,也就是每一个进程都有自己的虚拟地址空间.可是线程的PCB是共享的, ...
- Bootstrap定制开发
Bootstrap作为目前很受欢迎的前端框架,越来越多的网站开始使用基于Bootstrap框架进行开发. 1.定制开发方法 (1)Bootstrap定制开发可以使用LESS和Grunt实现定制化 (2 ...
- Java类中代码的执行顺序 静态代码块>构造代码块>构造方法
一:静态代码块 注意是代码块,不是静态函数.函数要调用才执行,代码块加载就执行,一般是静态变量的声明与初始化.被static修饰的代码块(赋值.输出操作等).类中静态语句块仅在类加载时被执行一次 如 ...
- kettle Spoon.bat运行闪退
1.情景展示 启动kettle的Spoon.bat闪退,并没有进入kettle的启动界面. 2.原因分析 使用条件: jdk版本需>=1.6: java需配置环境变量. 如果满足了上述前提条 ...
- 关于RSA加密算法的工具类
关于RSA加密算法的工具类 最近在捣鼓SSO(单点登录),就是一个在应用(系统)登录之后,当切换其他应用(系统)的时候,可以省去登录,提高用户的使用的便捷.(具体有时间在写) 期间涉及的安全问题,发送 ...