Groovy编程入门攻略

当一个Java开发人员加入到Groovy的开发之旅的时候,他/她经常带着Java思想去思考,并逐步地学习Groovy,每次学习一个特性,这会让他慢慢变得更有创造性和写出更符合语言习惯的Groovy代码。这篇文章的目的是引导这些开发人员去学习基本的Groovy编程风格,学习新的操作技巧,新的语言特性,例如闭包等等。这篇文章并不会详细铺开描述,而是给读者一个入门的指引,并让读者在以后的深入学习打好基础。如果你喜欢这篇文章,可以贡献你的一份力量去丰富它。

无分号

C / C++ / C# / Java开发者,经常到处使用分号。尽管Groovy支持99%的java语法,有时你只需简单的把java代码粘贴到Groovy程序里,但是却带着一大堆分号。在Groovy里,分号是可选的,你可以省略他们,更常用的用法是删除它们。

返回关键字 (Return) 变得可选

在Groovy的世界里面,方法的程序块结尾可以不写'return'关键字而照样返回值。尤其是对于语句不多的方法和闭包。这样的写法更优美更简洁:

String toString() {return"a server"}
String toString() {"a server"}

但有些情况却不那么优美,例如你使用了变量,并在两行里面出现两次:

def props() {
  def m1 = [a:1, b:2]
  m2 = m1.findAll { k, v -> v %2==0}
  m2.c =3
  m2
}

在这个例子里面,或者是在最后一个表达式后面加上一个空行,抑或是显式地加上'return'关键字会令代码更有可读性。

我自己个人习惯,有时候喜欢用return关键字,有时候又不喜欢,这跟个人口味有关系吧。但是,更多时候,例如在闭包里面,我更喜欢不写return关键字。所以即使return关键字是可选的,也不会强制你不能使用它,如果你认为它会打破代码的可读性。

谨慎为上,然而当你使用def关键字定义,而并非用代替具体某一个类型去定义的方法,有时候你会惊奇的发现最后一条表达式会作为返回结果而返回。所以更多时候更推荐使用具体的类型(例如void或类型)作为返回类型。在我们上面的例子中,如果我们忘记了把m2放在最后一行并作为返回值,那么最后的一个表达式将是m2.c = 3,这样会导致数值3作为返回值返回,而不是我们期待的结果。

形如if/else语句,try/cath语句同样可以返回值,因为它们里面都有"最后一个表达式"会被返回。

def foo(n) {
  if(n ==1) {
    "Roshan"
  }else{
    "Dawrani"
  }
}

assertfoo(1) =="Roshan"
assertfoo(2) =="Dawrani"

Def 和 类型

当我们讨论def和类型,我经常会发现一些开发人员既用'def'又用类型。但是'def'在这里是多余的。所以,大家要做一个选择,要不用'def', 要不就用类型。

所以不要写出如下的代码:

def String name = "Guillaume"

可以写成

String name ="Guillaume"

当我们在Groovy里面使用def,真正的类型是Object(所以你可以向用def定义的变量,赋值任何的对象,并且,当一个方法是用def作为返回值的时候,可以以任何对象类型作而返回)。

当一个方法未声明变量类型,你可以使用def,但是这不是必须的,所以可以省略他,所以可以代替下面的语句:

void doSomething(def param1, def param2) { }

推荐:

void doSomething(param1, param2) { }

但是,就如我们在文章末尾提到的,我们更推荐为方法的参数指定类型。这样可以帮助提高代码的可读性,也可以帮助IDE工具使代码完整,或者利用Groovy的静态类型检查或静态编译功能。

另外一个def多余的地方是定义构造函数,应避免使用:

class MyClass {
  def MyClass() {}
}

应去掉 构造函数前的'def':

classMyClass {
  MyClass() {}
}

默认的Public

默认情况,Groovy会认为类和方法是定义为'public'的。所以你不需要显式的声明public。当需要声明为非public的时候,你需要显式的指定修饰符。

所以避免如下写法:

public class Server {
  public String toString() {return "a server"}
}

推荐使用如下简洁的写法

class Server {
  String toString() {"a server"}
}

你可能会关心包范围内的可访问性问题。事实上,Groovy允许省略public, 是因为这个包范围里面默认情况下是不支持public,但是事实上有另外一个Groovy注释语法去实现可访问性:

class Server {
  @Package ScopeCluster cluster
}

省略圆括号

Groovy允许你在顶级表达式中省略圆括号,例如println语句:

println"Hello"
method a, b

对比:

println("Hello")
method(a, b)

当方法的最后一个参数是闭包的时候,例如使用Groovy的'each'迭代机制,你可以把闭包放在括号之外,甚至省略括号:

list.each( { println it } )
list.each(){ println it }
list.each { println it }

通常我们推荐使用上面第三种写法,它显得更自然,因为没有圆括号是多么的多余!

但是Groovy在某些情况并不允许你去掉圆括号。就像我之前说的,顶级表达式可以省略,但是嵌套的方法或者赋值表达式的右边,你却不能省略:

def foo(n) { n }
println foo1// 错误写法
def m = foo1

类,一级公民

在Groovy里面,.class后缀是不需要的,有点像Java的instanceof。

例如:

connection.doPost(BASE_URI +"/modify.hqu", params, ResourcesResponse.class)

下面我们使用GString,并使用第一类公民:

connection.doPost("${BASE_URI}/modify.hqu", params, ResourcesResponse)

Getters和Setters

在Groovy的世界里,getters和setters就是我们常说的"属性",Groovy提供一个注释语法捷径给我们去访问或给属性赋值。摒弃java方式的getters/setters,你可以使用注释语法:

resourceGroup.getResourcePrototype().getName() == SERVER_TYPE_NAME
resourceGroup.resourcePrototype.name == SERVER_TYPE_NAME

resourcePrototype.setName("something")
resourcePrototype.name = "something"

当你用Groovy写beans的时候,我们通常叫做POGOs(普通Groovy对象),你不需要自己创建属性,和getters/setters方法,而是留给Groovy编译器来完成。

所以,不再需要这样去写:

class Person {
  private String name
  String getName() {returnname }
  void setName(String name) {this.name = name }
}

而是:

class Person {
  String name
}

就如你看到的,一个简洁的"属性"并不带任何的访问修饰符,会让Groovy编译器为你自动生成一个私有的属性和getter和setter方法。

当你在Java中使用这样一个POGOs对象,getter和setter方法其实是存在的,当然跟正常的写法无异。

虽然编译器可以创建getter/setter方法,但是当你需要在这方法里面增加一些特殊的逻辑或者跟默认的getter/setter方法不一样,你可以自己去定义它,编译器自动会选择你的逻辑代替默认逻辑。

用命名参数和默认构造器初始化beans

有这样一个bean:

class Server {
  String name
  Cluster cluster
}

为避免像下面这样的麻烦:

def server =newServer()
server.name ="Obelix"
server.cluster = aCluster

你可以使用命名参数和默认构造器(首先会调用构造器,然后依次调用setter方法):

def server =newServer(name:"Obelix", cluster: aCluster)

在同一个bean里面使用with()语法处理重复操作

默认构造器中,命名参数在创建新的实例时是一件十分有趣的事情。但是当你需要更新一个实例时,你是否需要重复地在变量前重复敲打'server'这个前缀呢?答案是否定的,因为多亏了with()语句,Groovy会自动为你填上:

server.name = application.name
server.status = status
server.sessionCount =3
server.start()
server.stop()

对比:

server.with {
  name = application.name
  status = status
  sessionCount =3
  start()
  stop()
}

equals 和 ==

Java世界里,==就相当于Groovy里面的is()方法,另外Groovy的==就是聪明的equal()方法!

当你需要比较对象的引用的时候,你应该使用Groovy的==,因为他会帮你避开NullPointerException,而你就不需要关心操作符的左侧或右侧是否为null。

不应该这样写:

status !=null&& status.equals(ControlConstants.STATUS_COMPLETED)

而是:

status == ControlConstants.STATUS_COMPLETED

GStrings ()

我们经常再JAVA里面使用string和变量连接,并使用大量的双引号,加号,还有\n字符去创建新的一行。而使用插值字符串(在Groovy里面叫做GStrings),可以简化我们的代码写法:

throw new Exception("Unable to convert resource: "+ resource)

对比:
 
throw new Exception("Unable to convert resource: ${resource}")
在花括号里面,你可以放任何的表达式,而不单单是变量。对于简单的变量,或者变量属性,你甚至可以丢掉花括号:
 
throw new Exception("Unable to convert resource: $resource")
你甚至可以通过闭包注释(${-> resource})对表达式进行延迟计算。当GString变量强制转换为String变量的时候,它会自动计算闭包的值,并通过调用toString()方法作为返回值。例如:

int i =3

def s1 ="i's value is: ${i}"
def s2 ="i's value is: ${-> i}"

i++

asserts1 =="i's value is: 3"// 预先计算,在创建的时候赋值
asserts2 =="i's value is: 4"// 延迟计算,使用最新的变量值

在Java里面,字符串连接是十分的累赘:

throw new PluginException("Failed to execute command list-applications:"+
  " The group with name "+
  parameterMap.groupname[0] +
  " is not compatible group of type "+
  SERVER_TYPE_NAME)

你可以使用 \ 连续字符 (这不等同于多行文本)


throw new PluginException("Failed to execute command list-applications: \
The group with name ${parameterMap.groupname[0]} \
is not compatible group of type ${SERVER_TYPE_NAME}")

或者使用三个双引号实现多行文本:

throw new PluginException("""Failed to execute command list-applications:
  The group with name ${parameterMap.groupname[0]}
  is not compatible group of type ${SERVER_TYPE_NAME)}""")

另外,你也可以使用.stripIndent()函数实现删除多行文本里面的左侧多余空格。

同时请注意Groovy里面单引号和双引号的区别:单引号通常用来创建Java字符串,不带任何的插值变量,而双引号可以创建Java字符串,也可以用来创建带插值变量的GStrings。

对于多行字符串,你可以使用三个引号:例如在GStrings变量使用三个双引号,在简单的String变量中使用三个单引号。

如果你需要写正则表达式,你必须使用斜杠字符标记:

assert"foooo/baaaaar"==~ /fo+\/ba+r/

斜杠标记的好处是,你不需要写两个反斜杠去做转义,让正则表达式更简洁。

最后但并非不重要,请使用单引号去定义字符常量,使用双引号定义需要使用插值函数的字符串。

原生的数据结构语法

Groovy对数据结构提供原生语法,例如列表,映射,正则表达式或者一个范围内的数值。请在你的Groovy程序中好好使用它们。

以下是一些例子是使用那些原生数据结构的:

def list = [1,4,6,9]

// 默认情况下,键是字符的,所以没必要用引号括着它们
// 你可以使用with()包含着键,例如使用[(状态变量):状态名],利用变量或对象作为键
def map = [CA:'California', MI:'Michigan']

def range =10..20
def pattern = ~/fo*/

// 等同于 add()
list <<5

// 调用 contains()
assert4in list
assert5in list
assert15in range

// 下标符号
assertlist[1] ==4

// 增加键值对
map << [WA:'Washington']
// 下标符号
assertmap['CA'] =='California'
// 属性
assertmap.WA =='Washington'

// 使用正则表达式匹配字符串
assert'foo'=~ pattern

Grovvy 开发工具

我们来继续说说数据结构,当你需要迭代集合,Groovy提供非常丰富的方法,装饰java的核心数据结构,例如each{}, find{}, findAll{}, every{}, collect{}, inject{}.这些方法给编程语言增加了些乐趣,同时帮助我们更轻松的设计复杂的算法。通过装饰器,大量的新方法已应用于java的各种类型,这得得益于语言得的的动态特性。你可以查找更多的使用在字符串,文件,流,集合或者其他的方法:

http://groovy.codehaus.org/groovy-jdk/

switch的力量

Groovy的switch比C语言家族的更强大,因为它们通常只接收基本数据类型和。而Groovy的switch可以接受丰富的数据类型:

def x =1.23
def result =""
switch(x) {
  case"foo": result ="found foo"
  // lets fall through
  case"bar": result +="bar"
  case[4,5,6,'in List']:
    result ="list"
    break
  case 12..30:
    result ="range"
    break
  case Integer:
    result ="integer"
    break
  case Number:
    result ="number"
    break
  default: result ="default"
}
assert result =="number"

通常情况,使用isCase()方法可以判断一个数值是否为大小写。

别名导入 Import aliasing

Java,中当要使用来自两个不同包的同名class时,如

java.util.List 和 java.awt.List, 你能导入其中的一个类,而另一个你就不得不给其有效的重命名了.

还有时候我们在代码中经常使用一个名字非常长的类,使得整个代码变得臃肿不堪.

为了改善这些状况, Groovy 提供了别名导入的特性:

importjava.util.List as juList
importjava.awt.List as aList

importjava.awt.WindowConstants as WC   //import的时候 用 as 设置一个别名
当然也可以静态的导入某个方法:

import static pkg.SomeClass.foo
foo()

Groovy 的真值体系

在groovy中所有对象都可以被强制转为为一个boolean值,即: null, void 或空值 都 会视为 false, 其他的都视为 true.

所以不要再写 这种代码了:if (name != null && name.length > 0) {} 改为: if (name) {}

对于集合和其他数据类型同样适用.

因此,你可以在 诸如while(), if(), 三目运算符,Elvis 运算符(下面会讲)等等的判断条件中使用这种简单的方式.
更强悍的是可以给你的 类 添加 asBoolean() 方法,从而定制你这个类的真值的。

安全的操作对象图(嵌套的对象) Safe graph navigation

Groovy支持使用 . 操作符来安全的操作一个对象图.
Java中如果你对一个对象图中的某个节点感兴趣,想检查其是否为null的时候,经常会写出复杂的if嵌代码,如下:

if(order !=null) { //1
  if(order.getCustomer() !=null) { //2
    if(order.getCustomer().getAddress() !=null) { //3
      System.out.println(order.getCustomer().getAddress());
    }
  }
}

而使用Groovy的安全操作符 ?. 可以讲上述代码简化为:

println order?.customer?.address

太精妙了。

操作连上 每个?.操作符前面的元素都会做Null检查,如果为null则抛出 NullPointerException 并且返回 一个 null值

断言 Assert

要检查参数、返回值等,你可以使用 assert 语句。

和 Java 的 assert 对比,Groovy 的 assert 无需单独激活。

def check(String name) {
  // name non-null and non-empty according to Groovy Truth
  assertname
  // safe navigation + Groovy Truth to check
  assertname?.size() >3
}

你将注意到 Groovy 的 Power Assert 语句提供更好的输出,包括每个子表达式断言时不同值的图形化视图。

Elvis (埃尔维斯)  ?:  操作符给变量赋默认值

?: 是一个非常方便的给变量 赋 默认值的操作符,他是三目运算符(xx? a:b)的简写.
我们经常写如下三目运算代码:

def result = name !=null? name :"Unknown"//如果name为null时给'Unknown'的默认值,否则用原name的值

感谢Groovy的真值体系,null的检查可简单直接的通过 ‘name'来判断,null 会被转成 false.
再深入一点,既然总得返回'name',那在三目操作运算符中重复输入name两次就显得累赘了,可以将问号和冒号之间输入的重复变量移除,于是就成了Elvis操作符,如下:

def result = name ?:"Unknown"  //如果判断name为 false 则取 "Unknown",否则 取name之值<span></span>


捕捉任何异常

如果你真的不在乎在一个try代码块中抛出的异常,groovy中可以简单的catch所有这些异常,并忽略其异常类型. 所以以后像这种捕捉异常的代码:

try{
  // ...
}catch(Throwable t) {
  // something bad happens
}

可以写成捕捉任意异常 ('any' or 'all', 或者其他你认为是"任意"的单词):

try{
  // ...
}catch(any) {
  // something bad happens
}

可选类型 建议

最后我将讨论一下何时和怎样使用 '可选类型' 这个特性.
Groovy让你自己来决定是 显示使用强类型 还是 使用def 关键字 来申明你要的东西,那么怎么决定呢?

我有个相当简单的经验法则:

当你写的代码将会被作为公共API给别人使用时,你应当总是使用强类型的申明。

这将使得: 接口约定更加明确,避免可能的错误类型参数的传递,有利于提供更易读的文档,在编辑如:仅你能使用的私有方法时,强制类型 会有利于IDE的代码自动完成功能,或使IDE更容易推断对象的类型,然后你就能更加自如的决定是否使用明确的类型了

(0)

相关推荐

  • Groovy编程入门攻略

    当一个Java开发人员加入到Groovy的开发之旅的时候,他/她经常带着Java思想去思考,并逐步地学习Groovy,每次学习一个特性,这会让他慢慢变得更有创造性和写出更符合语言习惯的Groovy代码.这篇文章的目的是引导这些开发人员去学习基本的Groovy编程风格,学习新的操作技巧,新的语言特性,例如闭包等等.这篇文章并不会详细铺开描述,而是给读者一个入门的指引,并让读者在以后的深入学习打好基础.如果你喜欢这篇文章,可以贡献你的一份力量去丰富它. 无分号 C / C++ / C# / Java

  • SpringBoot整合Groovy脚本实现动态编程详解

    目录 Groovy简介 应用场景 集成与使用 第一步.与SpringBoot集成 1.pom.xml文件如下: 第二步.写出Groovy版本的“Hello World” 1.HelloWorld.groovy脚本代码 2.创建测试类GroovyTest.java 3.运行结果 第三步.传入变量与获取返回值 1.变量与返回值Groovy脚本代码 2.创建测试类GroovyTest2.java 3.运行结果 第四步.启动SpringBoot 1.创建SpringContextUtil.java 2.

  • 编程界主流脚本编程语言的比较和选择

    过去这一年的时间里,我买了不少书,查了很多资料,可以算是认真的学习了几种主流的脚本语言,因为我一直想搞一个好用的自动化工具,来方便我们的系统维护.虽然这个愿望还没有达成,但是在这个过程中,还是学到了很多东西.今天下午,跟同事们聊天时,说到了脚本语言,这是比较难得的,也正好借这个契机,把我的一些看法说一说,有不妥和错误的地方,请大家多指正. 为什么选择脚本语言 为什么选择脚本语言,可能每个人面对的实际情况都不一样.语言本身没有好坏之分,只有合适或者不合适.因此,我所谓的选择主要是根据应用需要来选择

  • 基于AngularJS+HTML+Groovy实现登录功能

    AngularJS是开发基于浏览器的响应式RWD应用程序的一个前端MVC框架,由谷歌最初开发的 开源项目,干净的架构吸引了大量粉丝,适合建立CRUD类型的业务应用程序,并不适合开发游戏等应用, 使用声明性编程的用户界面和命令式编程的逻辑, 支持现代桌面和移动浏览器 Internet Explorer版本8.0及以上. AngularJS是一款客户端MVC的javascript框架,而客户端MVC代表未来架构(为什么要使用MVC+REST+CQRS 架构),如果你有Struts或SpringMVC

  • 程序员编程从初级到中级的10个秘诀

    这个观点很好,有关程序员如何从初级跃升到中级的信息极少.以下是为了实现这种转变需要你去做的10件事. 1.学习另一门语言 其实你学的是哪一门语言并没有关系,但是学习另一门语言(不管你已经了解多少种语言)将把你打造为更好的程序员.能学会一门与你日常使用的语言风格迥异的语言则更佳.打个比方,如果你是C#程序员,学习VB.NET或者Java对你的帮助就没有学习Ruby或者Groovy大. 我说"学另一门语言"的意思是要真正学会它.学习一门语言包括三个领域的知识:语法.内置操作符和库,以及&q

  • 深入学习java中的Groovy 和 Scala 类

    前言 Java 传承的是平台,而不是语言.有超过 200 种语言可以在 JVM 上运行,它们之中不可避免地会有一种语言最终将取代 Java 语言,成为编写 JVM 程序的最佳方式.本系列将探讨三种下一代 JVM 语言:Groovy.Scala 和 Clojure,比较并对比新的功能和范例,让 Java 开发人员对自己近期的未来发展有大体的认识. Java 语言的开发人员精通 C++ 和其他语言,包括多继承(multiple inheritance),使得类可以继承自任意数量的父类.多继承带来的一

  • Android中AOP(面向切向编程)的深入讲解

    一.闲谈AOP 大家都知道OOP,即ObjectOriented Programming,面向对象编程.而本文要介绍的是AOP.AOP是Aspect Oriented Programming的缩写,中译文为面向切向编程.OOP和AOP是什么关系呢? 首先: l OOP和AOP都是方法论.我记得在刚学习C++的时候,最难学的并不是C++的语法,而是C++所代表的那种看问题的方法,即OOP.同样,今天在AOP中,我发现其难度并不在利用AOP干活,而是从AOP的角度来看待问题,设计解决方法.这就是为什

  • Android Studio 中运行 groovy 程序的方法图文详解

    Groovy简介 Groovy是一种基于JVM(Java虚拟机)的敏捷开发语言,它结合了Python.Ruby和Smalltalk的许多强大的特性,Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码.由于其运行在 JVM 上的特性,Groovy也可以使用其他非Java语言编写的库. Groovy 是 用于Java虚拟机的一种敏捷的动态语言,它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言.使用该种语言不必编写过多的代码,同时又具有闭包和动态语

  • Java调用groovy实现原理代码实例

    一.概述 Groovy is a multi-faceted language for the Java platform. Apache Groovy是一种强大的.可选的类型化和动态语言,具有静态类型和静态编译功能,用于Java平台,目的在于通过简洁.熟悉和易于学习的语法提高开发人员的工作效率.它可以与任何Java程序顺利集成,并立即向您的应用程序提供强大的功能,包括脚本编写功能.特定于域的语言编写.运行时和编译时元编程以及函数式编程. Groovy是基于java虚拟机的,执行文件可以是简单的

  • Java中的Gradle与Groovy的区别及存在的关系

    目录 一.Gradle构建的利与弊 二.Groovy的优点 三.依存关系 四.认识build.gradle 五.Gradlebuildscript 六.什么是闭包 七.Gradle只是闭包 八.探索Gradle依赖项配置 九.打包Gradle版本 十.任务 前言: 在Java项目中,有两个主要的构建系统:Gradle和Maven.构建系统主要管理潜在的复杂依赖关系并正确编译项目.还可以将已编译的项目以及所有资源和源文件打包到.war或.jar文件中.对于简单的构建,Maven和Gradle之间的

随机推荐