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
,可以看到如下图
到这里,就安装好了。
Groovy IDE
Hello Groovy
运行groovyconsole
之后,稍等,可以看到启动的Groovy
自带的编辑器。输入第一句代码:
println "Hello Groovy!"
然后按CTR+R
就可以得到如下输出
groovy> println "Hello Groovy!"
Hello Groovy!
或者我们save as
到一个相应的位置保存为hello.groovy
,然后cd
到相应的目录下面,运行groovy hello.groovy
,得到结果也一样,如下图
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);
}
}
输出结果为
- 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)