Groovy快速入门

Groovy简介

Groovy是基于Java平台开发的一门强大的、具有Optional类型,多功能性的动态语言,它具有静态类型和静态编译的功能。为了提高在java平台的开发效率,它的语法设计的很简洁,易学。和java无缝集成,从而使自己的项目具有强大的特性,比如脚本功能、DSL(Domain-Specific Language)语言编写,运行时和编译时元编程(meta-programming)以及函数式编程。

Groovy优势

  • 语法简洁,可读性强,并且很容易上手
  • 功能强大,具有闭包特性,自动构建,元编程,函数式编程,Optional类型以及静态编译的功能
  • 集成性强,与Java语言或者一些第三方库无缝接入
  • DSL语言编写,语法灵活,扩展性强,使项目具有高级配置与可定制的机制,可读性强的特点
  • 完整的生态系统,涉及Web开发、reactive应用,并发性、异步的库、测试框架、构建工具(Build Tools)、代码分析、GUI构建
  • 脚本测试,可以写一些简单的、可维护的测试用例,来完成自动化构建任务

安装Groovy

Groovy下载链接
下载后,把压缩包解压到相应的位置,然后再环境变量的Path里面添加解压后的路径(到解压的bin目录下)。配置好后,然后运行groovy -v,可以看到如下图

图1.png

到这里,就安装好了。

Groovy IDE

图2.png

Hello Groovy

运行groovyconsole之后,稍等,可以看到启动的Groovy自带的编辑器。输入第一句代码:

println "Hello Groovy!"

然后按CTR+R就可以得到如下输出

groovy> println "Hello Groovy!" 
 
Hello Groovy!

或者我们save as到一个相应的位置保存为hello.groovy,然后cd到相应的目录下面,运行groovy hello.groovy,得到结果也一样,如下图

图3.png

Java&Groovy不同点

  • 默认含有import ,引入包无须在前面再申明import,比如java.io.*java.lang.*等等
  • 方法多元化(Multi-methods)Groovy的参数类型决定于代码运行时,Java正好相反,Java参数类型决定于编译时,主要用申明的类型决定。比如举个例子
    Hello.groovy
void method(String arg){
    println "this arg is string type";
}
void method(Object arg){
    println "this arg is Object type";
}

Object obj = "hello Groovy";
method(obj);

输出结果为

this arg is string type

Hello.java

public class Hello{
    void method(String arg){
        System.out.println("this arg is string type");
    }
    void method(Object arg){
        System.out.println("this arg is Object type") ;
    }
    public static void main(String[] args){
        Object obj = "hello Groovy";
        Hello hello = new Hello();
        hello.method(obj);
    }
}

输出结果为

图4.png
  • Array初始化

java初始化一个Array,一般是这样

int[] array = {1,2,3};

但是在Groovy里面{...}代表的是一个闭包(closures),所以Groovy不能和Java这样申明。我们应该这样申明

int[] array = [1,2,3]
  • 包范围可视性

    比如一个类

class Person{
String name
}

在`Groovy`里面,没有可视化修饰符的属性,都是公有的,并且会生成`getter`和`setter`方法。如果要私有化这个属性,可以加注解`@PackageScope`,如下

class Person {
@PackageScope String name
}

- ARM 块
  `ARM(Automatic Resource Management)`自动化资源管理,在`Java7`开始引入,但是`Groovy`不支持,所以`Groovy`通过闭包的形式提供了各种各样的依赖方法来替换,实现效果是一样的。比如:

Path file = Paths.get("/path/to/file");
Charset charset = Charset.forName("UTF-8");
try (BufferedReader reader = Files.newBufferedReader(file, charset)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}

} catch (IOException e) {
e.printStackTrace();
}

可以写成:

new File('/path/to/file').eachLine('UTF-8') {
println it
}

或者写成和上面`Java`版本更像的代码:

new File('/path/to/file').withReader('UTF-8') { reader ->
reader.eachLine {
println it
}
}

- 内部类
    `Groovy`的匿名内部类和内嵌类和`Java`的实现方式是一样的,但是 `Groovy`不支持`y.new X()`这种实现方式,需要使用 new X(y),如下
`Java`

public class Y {
public class X {}
public X foo() {
return new X();
}
public static X createX(Y y) {
return y.new X();
}
}

`Groovy`

public class Y {
public class X {}
public X foo() {
return new X()
}
public static X createX(Y y) {
return new X(y)
}
}

-  Lambda 表达式
`Java8`是支持`Lambda`表达式的,比如

Runnable run = () -> System.out.println("Run");
list.forEach(System.out::println);

但是`Groovy`是不支持,取而代之的是使用闭包实现的一样的效果

Runnable run = { println 'run' }
list.each { println it } // or list.each(this.&println)

- GStrings

在`Java`里面字符串是`"xxxxx"`这样的形式,但是在`Groovy`里面这种形式叫`GString(Groovy String)`或者`String`类型,当使用```($)```符号引用类型是,它会自动转换为`GString`类型,反之,为`String`类型。

- 字符串和字符字面量
当使用```'xxx'```形式使用的时候,它的类型为字符串`(String)`,当使用```"xxx"```的时候,参考上面`GString`说明

- 基本类型和封装性
由于`Groovy`一切类型皆对象,所以它会自动把基本类型封装成对象,这一点和`Java`规范不同,比如

int i
m(i)
//方法1
void m(long l) {
println "in m(long)"
}
//方法2
void m(Integer i) {
println "in m(Integer)"
}

如果是`Java`,自然会去执行方法1,但是`Groovy`则会执行方法2

- ==的不同

在`Java`中`==`指的是基本类型相等或者是对象的引用相等,但是在`Groovy`中,`Java`的`==`含义转换为`a.compareTo(b)==0`,如果两边都实现了`Comparable`接口的话,而`Groovy`的`==`的含义指的是`a.equals(b)`,对象引用是否相等,则是使用`a.is(b)`

- 转换
`java`的自动转换只能向下转换,`Groovy`则进行相应的扩展,具体如下


![图5.png](http://upload-images.jianshu.io/upload_images/2148217-7f013db57c0df6c2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

   `Y` 表示 `Groovy` 可以执行的转换。`D `表示的是在动态编译或显式转换时 `Groovy` 能够执行的转换。`T` 表示 `Groovy` 可以执行的转换,但数据被截断了。`B `表示装箱/拆箱操作。`N `表示` Groovy `不能实行的转换。

- 扩展的关键字

as

def

in

trait

#Groovy开发套件

#### IO操作

`Groovy`提供了一些`IO`操作的帮助类,这里提供几个常用的API
1.  [java.io.File](http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html)
2.  [java.io.InputStream](http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html)
3.  [java.io.OutputStream](http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/OutputStream.html)
4.  [java.io.Reader](http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Reader.html)
5.  [java.io.Writer](http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Writer.html)
6.  [java.nio.file.Path](http://docs.groovy-lang.org/latest/html/groovy-jdk/java/nio/file/Path.html)


- 读文件
通过`Groovy`读取文件很简单,举个例子,先在`E:/Android/Groovy/workspace`目录下创建一个`read.txt`文件,然后编写代码

new File('E:/Android/Groovy/workspace','read.txt').eachLine{ line ->
println line
}

`File`对象有两个参数,一个是文件的路径,一个是文件的文件名,然后`eachLine`方法就可以读取到文件内容

这是通过Groovy的IO方法读取的一个txt文件,

其实,在`File`的`API`里面还可以查到`eachLine`还有很多个参数

![图6.png](http://upload-images.jianshu.io/upload_images/2148217-8c51380a2eb4645e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

在`Groovy`里面,使用`IO`操作,即使代码出现异常,`IO`读取流也会保证会被关闭。比如

def count = 0, MAXSIZE = 3
new File(baseDir,"haiku.txt").withReader { reader ->
while (reader.readLine()) {
if (++count > MAXSIZE) {
throw new RuntimeException('Haiku should only have 3 verses')
}
}
}

- 写文件

new File('E:/Android/Groovy/workspace','write.txt') << '''Groovy is Good,
Groovy is Nice
Groovy is Beautiful!'''

在相应的位置就可以找到`write.txt`,然后内容为

Groovy is Good,
Groovy is Nice
Groovy is Beautiful!

####集合
###### List操作
- 取元素
```def emptyList = []//空List```
```def list = [1,5,7,8]```
```list.get[2] = 7 ```
```list.getAt(2) = 7 ```
```list[2] = 7 ```
 ```list[-2] = 7 ```
```list[1..2] = [5,7] ```
```list[1..<7] = [5,7]```
-  迭代元素

list.each{
println "Item: $it"
}

list.eachWithIndex{
item,i ->
println "$i:$item"
}

`Groovy`映射功能,通过`collect`从当前集合映射到另个集合

def list = [1,5,7,8]
def newList = list.collect{it.multiply(2)}.eachWithIndex{
item,i ->
println "$i:$item"
}

输出

0:2
1:10
2:14
3:16

- 过滤和搜索

```list.find{it>5} == 7 ```
```list.findAll{it>5} == [7,8]```

通过一个`list`去搜索匹配

['a','b','c','d','e'].findIndexOf{
it in ['k','e','g']
} == 4

```['a','b','c','d','e'].indexOf('c') == 2```
```['a', 'b', 'c', 'd', 'c'].indexOf('k') == -1//list不存在元素则返回-1```
```['a', 'b', 'c', 'd', 'd'].lastIndexOf('c') == 4```
```[1, 6, 3].every { it < 5 } == false//集合所有元素小于5则返回true```
```[1, 2, 4].any { it > 3 } //集合任何一个元素大于3则返回true```
```[1, 2, 3, 4, 5, 6].sum() == 21//求和```
```[1, 2, 3].join('-') == '1-2-3'//加入间隔符并转换为String```

[1, 2, 4].inject(3){
count ,item -> count + item
} == 10 // 注入对应的元素 3+1+2+4

```[1, 2, 3].max() = 3```
```[1,2,3].min() = 1 ```
- 添加和删除元素

```[1, 2, 4] << 5 == [1,2,4,5] ```
```[1, 2, 4] << 5 << 7 << 8 == [1, 2, 4, 5, 7, 8] ```
``` [1, 2, 4] << [5,8] ==  [1, 2, 4, [5, 8]] ```
```[1, 2] + 3 + [4, 5] + 6 == [1, 2, 3, 4, 5, 6]```
```[1, [2, 3, [4, 5], 6], 7, [8, 9]].flatten() == [1, 2, 3, 4, 5, 6, 7, 8, 9]```
```['a','b','c','b','b'] - 'c' == ['a','b','b','b']```
-  排序
```[6, 3, 9, 2, 7, 1, 5].sort() == [1, 2, 3, 5, 6, 7, 9]```
- 复制元素
```[1, 2, 3] * 3 == [1, 2, 3, 1, 2, 3, 1, 2, 3]```
```[1, 2, 3].multiply(2) == [1, 2, 3, 1, 2, 3]```
 ```Collections.nCopies(3, 'b') == ['b', 'b', 'b']```

###### Map操作
其中`Map`操作和`List`操作的方法差不多,只不过`Map`是通过获取`key`来操作的,而`List`是通过index获取,这点和`Java`里面的集合操作是一样的

def map = [Name:'Goach',Sex:'man',Age:'24',]
map.each{
entry -> println "key:$entry.key value:$entry.value"
}
map.eachWithIndex{
entry ,i -> println "$i - key:$entry.key value:$entry.value"
}
map.each{
key,value -> println "key:$key value:$value"
}
map.eachWithIndex{
key,value,i -> println "$i -key:$key value:$value"
}

####ConfigSlurper
`ConfigSlurper` 是`Groovy`里面一个读取配置文件的帮助类,和`Java *.properties`一样。`ConfigSlurper`允许使用`.`或者以闭包的形式配置值得作用域和任意的对象类型,比如

def config = new ConfigSlurper().parse('''
//.的形式
app.date = new Date()
app.sex = 'm'
//闭包的形式
app {
age = '23'
name = 'Goach'
}
''')
println "date:$config.app.date; sex:$config.app.sex;"+
"age:$config.app.age; name:$config.app.name"

输出为:

date:Thu Jun 08 14:15:33 CST 2017; sex:m;age:23; name:Goach

上面的的字符串我们也可以用文件形式读取,比如

def config = new ConfigSlurper().parse(new File("config.groovy").text)

#MOP
`Groovy`也具有元对象协议的特性```(Meta Object Protocol,MOP)```,实现这个特性,有个很重要的类就是`groovy.lang.GroovyObject`。基本上`Groovy`的对象都直接或者间接的实现了它。`GroovyObject`有两个很重要的方法`invokeMethod()`和`getProperty()`,当一个对象调用一个方法或者属性的时候,而这个方法或者属性不存在的时候,这两个方法就会被调用。比如

class Person{
def invokeMethod(String method,Object params){
println "call invokeMethod ${method}"
if(params != null){
params.each{
println "\tparams ${it}"
}
}
}
def getProperty(String property){
println "property:${property}"
}
}
def person = new Person()
person.sayHello("hello")
person.name

输出为

call invokeMethod sayHello
params hello
property:name

当这个方法不存在的时候,它就会进行拦截,然后智能的输出响应的对象。同时会调用`methodMissing`这个方法

#类
- 添加方法
当为一个类添加方法的时候,一般可以使用`<<`或者`=`运算符来添加方法,比如

class Car{
String name
}
Car.metaClass.run << { -> println "$name在路上跑"}

def car = new Car(name:"宝马")

car.run() == "宝马在路上跑"


- 添加属性

Car.metaClass.speed = '120km/h'

- 构造函数

class Food{
String name
}
Food.metaClass.constructor << { String name -> new Food(name:name)}

def food = new Food('apple')

println "food name is ${food.name}"

- 静态方法

class Book {
String title
}

Book.metaClass.static.create << { String title -> new Book(title:title) }

def b = Book.create("The Stand")

- 间接调用方法

class Person{
String name
}
class Action{
def sayHello(){
"Hello!"
}
}
def action = new Action()
Person.metaClass.hello = action.&sayHello
def person = new Person()
println person.hello() == "Hello!"

- 动态方法名称

class Person{
String name = "Goach"
}
def methodName = "Lili"

Person.metaClass."changeNameTo${methodName}" = {-> delegate.name = "Lili"}

def person = new Person()
println person.changeNameToLili() == "Lili"

`Groovy`更多的使用方法,例如`AST`转换,`Grape`的依赖,写一些测试用例,`DSL`语言以及`Groovy`在`Java`里面的常用方法可以[参考官网](http://www.groovy-lang.org/documentation.html),由于篇幅的原因,入门的简单学习就暂时到此为止。

#参考文献
[Groovy官网](http://www.groovy-lang.org/)
[Groovy1.6的新特性](http://www.infoq.com/cn/articles/groovy-1-6/)
[Groovy入门](http://wiki.jikexueyuan.com/project/groovy-introduction/)
[实战Groovy](https://wizardforcel.gitbooks.io/ibm-j-pg/content/8.html)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容

  • Groovy是一门基于JVM的动态语言,很多语法和Java类似。大部分Java代码也同时是合法的Groovy代码。...
    乐百川阅读 3,590评论 0 15
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,654评论 18 139
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,201评论 9 118
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,625评论 18 399
  • 请罗列Linux发行版的基础目录名称、命名法则及功能规定a./boot:引导文件存放目录,内核文件(vmlinuz...
    maomao妞阅读 209评论 0 0