Groovy 基础知识01

Groovy支持Java的语法并保留了Java的语义,所以我们可以自由的使用Groovy和Java而不必过分在意Groovy的语法,一般的一个java文件直接改成groovy文件就可以直接运行.这样我们就可以很方便的逐渐从Java入手转向更加有表达力的Groovy.因为Groovy语法更加简洁,更加有表达力
Groovy和Java相比有很多优点,不过有一个地方值得大家注意的是,Groovy由于它的动态特性在运行速度上是比不过Java的,一般性能损失在10~30%之间,当然这只是很久以前的数据,我没有自己仔细的对比,随着JDK和GDK的升级以及JVM的逐渐优化,我相信现在Groovy的性能损失已经小了多,至少不比Python/Ruby这里语言慢.所以不是极端的考虑性能,大家可以放心的使用.

Java –> groovy

我们以一个java例子开始,逐渐的转向Groovy.演示Java程序员怎么逐步的重构自己的代码为Groovy来使程序更加优雅

HelloWorldJava.java

1
2
3
4
5
6
7
8
public class HelloWorldJava {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println("i = " + i);
}
System.out.println("end");
}
}

我们直接改HelloWorldJava.javaHelloWorldgroovy.java

1
2
3
4
5
6
7
8
public class HelloWorldGroovy {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println("i = " + i);
}
System.out.println("end");
}
}

运行发现其实没有任何区别,证明了Java在大多数情况下可以直接转换为Groovy.

但是如果我们只是这样简单的改文件类型,还是难以发掘Groovy的语法优势,那么我们继续修改HelloWorldgroovy.java

HelloWorldgroovy.java

1
2
3
4
5
6
7
8
9
10
// public 在groovy中是可选的,类和方法默认就是public,所以我们可以去掉
// ;也是可选的,groovy默认一行为一个语句,换行意味着语句结束,除非一行有多个语句,否则我们可以不写分号
class HelloWorldGroovy {
static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println("i = " + i)
}
System.out.println("end")
}
}

继续改进,这个时候就开始和Java不一样的了,代码的信噪比就高了很多.

1
2
3
4
5
6
class HelloWorldGroovy {
static void main(String[] args) {
for (i in 0..10) println("i = " + i)
System.out.println("end")
}
}

继续改进

1
2
3
4
// 直接去掉外面的主函数等无关核心逻辑的代码,这时Groovy被自动认作脚本被执行
// 显然这样的代码,信噪比就高到不知那里去了
for (i in 0..10) println("i = " + i)
System.out.println("end")

Groovy并未限制我们使用java已有的各类循环语法,但是Groovy对循环做出了更加优雅的改进.使代码看上去和Python/Ruby一样简洁

例如,对于Integer类,Groovy提供了uptp()方法帮助简化迭代,上面的代码我们继续Groovy花

1
2
3
4
// 这样,不仅代码更加少了,而且很明晰的表达了代码的意图,可读性很好
// it是groovy里面的默认入参,当一个闭包没有显式的给入参指明名称的时候,这个入参默认就叫it
0.upto(10) { println("i = " + it) }
System.out.println("end")

此外,如果我们想多次做重复的操作,Groovy也为我们提供了times方法,让我们避免使用for循环

1
3.times { println("hello groovy,time=" + it) }

输出

1
2
3
hello groovy,time=0
hello groovy,time=1
hello groovy,time=2

我们也可以设置迭代的步长

1
2
// 这会打印出0到10之间的全部的偶数,步长为2
0.step(10, 2) { println("hello groovy num= " + it) }

其实,Groovy developer kit(GDK)相比于JDK并不是完全的重构,而是在JDK基础上做一些改进.GDK通过对JDK的扩充,实现了Groovy的一些新特性.这也是为什么我们开发Groovy的时候建议也要安装JDK的原因.

安全导航操作符?

相信有经验的程序员都知道,null是一个价值几千万美元的错误,虽然这是一个幽默的笑话,但是的确如此.我们在写Java的时候,经常会和各种各样的空指针异常做斗争,这样的操作过于频繁而且必要,导致Apache和Google都推出了相应的工具类帮助我们避免这类错误.那么对于Groovy来讲,除了延续Java的那一套工具,有没有其他的好的方法呢?答案是有的,这个就是安全导航操作符?,没错就是?

1
2
3
4
5
6
7
8
9
10
// Groovy是类型可选的,我们声明函数的时候,可以直接 def 后面接函数名称
//函数的入参也可以不指定具体的类型,Groovy代码在运行时会自动的推导出正确的类型
//当然你指定了类型也没有关系,指定了更好在很多情况下有助于辅助程序作出正确的判断
def checkAndPrint(str) {
str?.reverse()
}

println(checkAndPrint("ABC")) // 输出 "CBA"
println(checkAndPrint("")) // 输出 ""
println(checkAndPrint(null)) // 输出 null

优雅的异常

Java是一种很谨慎的语言,这在企业级程序开发中有助于减少没有过多经验的程序员的犯错概率,遇到可能的异常,Java强制要求我们必须处理,要么捕获异常要么向上抛出异常.但是有时候对有经验的Java程序员来讲就未必了,相反他们认为代码异常的繁琐和丑陋.

例如我们使用Java风格来处理异常一般是这样的,看起很丑

1
2
3
4
5
6
7
8
9
class HelloWorldGroovy {
static void main(String[] args) {
try {
Thread.sleep(5);
} catch (Exception e) {
e.printStackTrace();
}
}
}

那么我们使用Groovy怎么处理呢.Groovy并不强制的要求立即处理异常,而是默认的在出现异常的时候向上层抛出异常,任何Groovy代码层面上不显示处理的异常,都会自动的传递给上一层,Groovy让程序员在必要的时候自己去处理异常,而不是时时刻刻提醒并制造啰嗦的代码

Groovy入门总结

  1. return语句是可选的,groovy默认最后一行语句的结果为返回值
  2. 分号是可选的,尽管我们可以使用分号,但是除非使用分号来分割一行里面的多条语句否则是没有必要的
  3. 方法和类默认是public
  4. ?操作符只有在对象不为null的时候才会继续调用后续的逻辑
  5. 函数参数的类型是可选的,甚至入参的名称也是可选的,默认有一个it作为方法的入参
  6. groovy不强制要求程序员立即处理异常,而是把默认向上传递异常,将异常的处理选择权交给程序员
  7. 静态对象里面可以使用this来引用Class对象

JavaBean和GroovyBean

javabean 是我们常用的一个概念,它提供get/set方法并隐藏javabean的具体的属性,做到了属性的封装. 这个方式非常好,但是很多情况下显得很啰嗦,后来也出现了一些工具例如lombok帮我们做这些事情.那么groovy会有改进么

实际上,javabean这种概念在Groovy中没有被否定,只是换了一种很简单的语法来减少代码的啰嗦的问题

javabean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//看起来set/get这些模板代码非常的冗余的,但是又有必要
public class HelloWorldJava {
private float price;
private final Integer num;

public HelloWorldJava(float price, Integer num) {
this.price = price;
this.num = num;
}

public float getPrice() {
return price;
}

public void setPrice(float price) {
this.price = price;
}

public int getNum() {
return num;
}

public static void main(String[] args) {
HelloWorldJava h = new HelloWorldJava(12.0f, 5);
System.out.println(h.getPrice() + " : " + h.getNum());
}
}

groovybean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//简洁多了
class HelloWorldGroovy {
def price
final Integer num

HelloWorldGroovy(num, price) {
this.num = num
this.price = price
}

static void main(String[] args) {
def h = new HelloWorldGroovy(5, 12.0f)
println(h.price + " : " + h.num)
}
}

在Groovy我们可以使用def来声明一个属性或者函数,当然也可以像Java那样指定类型.Groovy会默默的为我们创建get/set方法,当代码中调用一个属性的时候,其实并不是直接的使用这个属性而是使用自动生成的get方法. 同时如果我们想把属性设置为只读的,那么我们可以使用final来修饰这个属性,groovy就不会为这个被修饰的属性再生成set方法了.不过要注意的一点是,Groovy中是没有java里面常见的private概念的,如果我们想拒绝一个属性被修改,那么要么修饰为final这样在强行被修改的时候会报错,或者我们自己手写一个set方法覆盖groovy自动生成的set,在自己的set方法里面拒绝对属性的修改