原文 http://groovy.codehaus.org/Groovy+style+and+language+feature+guidelines+for+Java+developers
Groovy编程风格和语言特性指南(针对Java开发人员)
作为一个Java开发人员,当他/她开始学习Groovy时,总是会用Java的方式,循序渐进地学习Groovy,以使他/她的工作效率提高和写出更地道的Groovy代码。本文的目的是引导这类开发人员学习一些常用的的Groovy语法风格,学习新的运算符,新的特性如闭包,等等。本文不是详细描述,只能作为一个快速的介绍,和进一步学习的基础部分,欢迎添加,丰富该文档。
没有分号
对于一个有C / C + + / C#/ Java背景的开发人员,我们是如此习惯使用分号,它们无处不在。更糟的是,Groovy支持99%的Java的语法,并且某些时候复制一些Java代码到你的Groovy程序里是如此的容易,这样你的世界就会充斥着无数的分号。但是......在Groovy中,分号是可选的,你可以忽略它们,将其删除是更地道的Groovy风格。
可选关键字return
在Groovy中,方法体内的最后一个表达式会被返回,而不需要return关键字。尤其是针对简短的方法和闭包,省略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。所以,即使关键字是可选的,但是不意味着强制性的,如果你认为它妨碍你代码的可读性就别使用它。
注意,如果使用“def”关键字而不是某个具体类型定义的方法时,有时返回的最后一个表达式可能会让你吃惊。所以通常更喜欢使用一个特定的返回类型,如void或某个类型。在我们上面的例子中,假设我们忘了把m2最为最后一条语句返回,最后的表达式将是m2.c == 3,这将返回... 3,而不是你期望的map。
有一些语句,如if / else语句,try / catch语句,也可以返回一个值,因为他们也可以有一个“最后表达式”:
def foo(n) {i
f(n ==1) {
"Roshan"
}else {
"Dawrani"
}
}
assert foo(1) =="Roshan"
assert foo(2) =="Dawrani"
Def和具体类型
我们正在谈论def和类型,我经常看到开发人员把'def'和具体类型一起使用。但是,“def”在这里是多余的。需要做出一个选择,要么使用“def”,要么使用一个具体类型。
所以,不要这样写:
def String name ="Guillaume"
较好的是这样:
String name ="Guillaume"
在Groovy中,def表明的实际类型是Object(这样你就可以给def定义的变量分配任何对象,和在用def定义的方法里返回任何类型的对象)。
默认的public修饰符
默认情况下,Groovy中认为,类和方法是具有public访问权限的。所以,你没有必要使用public修饰符。只有当它不是public的,你才应该使用可见性修饰符。
因此,对于以下写法:
public class Server { public String toString() {return "a server" } }
更简洁的写法是:
class Server { String toString() {"a server" } }
你可能想知道包范围的可见性修饰符,事实上,Groovy允许省略public,即表示默认情况下不支持包范围的可见性,但实际上有一个特殊的Groovy注解,能让你使用的包可见性:
class Server { @PackageScope Cluster cluster }
省略圆括号
Groovy允许你省略顶级层次表达式的圆括号,如用println的命令:
println "Hello"
method a, b
VS:
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 // won't work
def m = foo1
类,一等公民
后缀.class有点像Java的的instanceof,但在Groovy中已不再需要。
例如:
connection.doPost(BASE_URI +"/modify.hqu", params, ResourcesResponse.class)
使用的GString和一等公民,写法如下:
connection.doPost("${BASE_URI}/modify.hqu", params, ResourcesResponse)
Getter和Setter
在Groovy中,一个getter和setter方法形成了我们所谓的“属性”,并提供了一个捷径来访问和设置这些属性。和Java的调用方式不同,你可以使用一个类似字段访问的方式:
resourceGroup.getResourcePrototype().getName() == SERVER_TYPE_NAME
resourceGroup.resourcePrototype.name == SERVER_TYPE_NAME
resourcePrototype.setName("something")
resourcePrototype.name = "something"
用Groovy编写的bean,通常被称为POGOs(Plain and Old GroovyObjects),你不必创建字段的getter / setter,编译器会替你做。
因此,不要这样写:
class Person {
private String name
String getName() {
return name
}
void setName(String name) {
this.name = name
}
}
可以简单的写成:
class Person { String name }
正如你看到的,一个独立的没有修饰符的“字段”实际上使得Groovy编译器生成一个私有字段和相应的getter和setter。
当然,在Java中使用POGOs时,getter和setter确实存在,并可以照常使用。
尽管编译器会创建通用的getter / setter方法的逻辑,然而如果你愿意在这些getter / setter方法中做任何附加的或不同的逻辑,你必须可以提供它们,编译器将使用你的逻辑,而不是默认生成。
使用命名参数和默认构造函数初始化bean
一个bean,如:
class Server { String name; Cluster cluster }
你可以使用命名参数的默认构造函数(首先构造函数被调用,然后依次调用setter):
def server =new Server(name:"Obelix", cluster: aCluster)
而不是在声明后调用各个setter:
def server =new Server()
server.name ="Obelix"
server.cluster = aCluster
使用with()重复操作同一个bean
使用命名参数的默认构造函数创建新的实例很有趣,但如果你正在更新的一个得到的实例,你一定要重复一遍又一遍的'server'前缀?不,多亏了Groovy给所有对象添加的with()方法:
server.name = application.name
server.status = status
server.sessionCount = 3
server.start()
server.stop()
VS:
server.with {
name = application.name
status = status
sessionCount = 3
start()
stop()
}
Equals和 ==
Java的==其实是Groovy的is()方法,Groovy的==是一个聪明的equals()方法!
要比较的对象引用,你应该使用a.is(B),而不是==。
对于通常的equals()比较,你应该更喜欢Groovy的==,因为它也可以避免NullPointerException异常,无论左边或者右边的变量是不是null。
不要这样写:
status !=null && status.equals(ControlConstants.STATUS_COMPLETED)
这样做:
status == ControlConstants.STATUS_COMPLETED
GString(内插变量,多行)
Java中我们经常使用双引号,加好和\n换行符来使用字符串和变量串联。利用内插字符串(称为GString),这样的字符串看起来更好并且减少打字的痛苦:
throw new Exception("Unable to convert resource: " + resource)
VS:
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++
assert s1 =="i's value is: 3" // eagerly evaluated, takes the value on creation
assert s2 =="i's value is: 4" // lazily evaluated, takes the new value into account
Java中字符串和其连接的表达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字符串,也可以创建GString当有内插变量时。
对于多行字符串,你可以使用三个引号,也就是说:GString用三双引号,单纯的字符串用三个单引号。
原生语法的数据结构
Groovy提供原生的语法来构造如list,map,正则表达式,和范围值等数据结构。确保在你的Groovy程序利用它们。
下面是一些例子:
def list = [1,4,6,9]
// by default, keys are Strings, no need to quote them
// you can wrap keys with () like [(variableStateAcronym): stateName] to insert a variable or object as a key.
def map = [CA:'California', MI:'Michigan']
def range =10..20
def pattern = ~/fo*/
// equivalent to add()
list <<5
// call contains()
assert 4 in list
assert 5 in list
assert 15 in range
// subscript notation
assert list[1] ==4
// add a new key value pair
map << [WA:'Washington']
// subscript notation
assert map['CA'] =='California'
// property notation
assert map.WA =='Washington'
// matches() strings against patterns
assert 'foo' =~ pattern
Groovy的开发工具包
当你需要对集合进行迭代并关注在数据结构上时,Groovy中提供各种额外的方法,包装了Java的核心数据结构,就像each{},find{},findAll{},every{},collect{},inject{}。这些方法添加了函数编程的风格,并编写复杂的算法更容易。由于语言的动态性质,经过包装,许多新的方法应用到不同的类型。你可以找到很多关于字符串,文件,流,集合的非常有用的方法,更多请看:
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,'inList']:
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()方法的类型,也可以出现在case子句中。
Import使用别名
在Java中,当使用不同包里的具有相同的名称的两个类时,如java.util.List和java.awt.List,你可以导入一个类,但另一个必须使用全名。
还有,有时候在你的代码中,当频繁使用一个很长的类名时,代码会变得冗长。
为了改善这种情况下,Groovy允许import使用别名:
import java.util.List as juList
import java.awt.List as aList
import java.awt.WindowConstants as WC
你还可以导入静态的方法:
import static pkg.SomeClass.foo
foo()
Groovy的True
所有对象都可以被“强转成”一个布尔值:一切null,void或empty的都是false,如果不是,则计算结果为true。
因此,if (name) {}写法完全可以替代if (name != null && name.length > 0) {}。集合类也是如此。
于是,你可以在类似while(),if(),三元运算符,Elvis操作符(见下文)等中运用此技巧。
你甚至可以自定义的Groovy的true,通过添加一个的布尔asBoolean()方法到你的类中!
安全引用导航
Groovy支持.运算符的一个变体,可以安全导航一个对象图。
在Java中,当你对对象路径图中的一个节点感兴趣,你需要检查是否为NULL,这往往导致写出来的复杂的if语句或嵌套的if语句:
if (order !=null) {
if (order.getCustomer() !=null) {
if (order.getCustomer().getAddress() !=null) {
System.out.println(order.getCustomer().getAddress());
}
}
}
利用?.安全引用操作符,可以简化代码:
println order?.customer?.address
整个调用链中会检查空值,任何元素为null都不会抛出NullPointerException异常,如果有一个为null则返回值也是null。
断言
要检查参数,返回值,以及更多的东西,你可以使用“assert”语句。
与Java的断言相反,断言并不需要被激活才能工作,Groovy中它总是打开的。
def check(String name) {
// name non-null and non-empty according to Groovy Truth
assert name
// safe navigation + Groovy Truth to check
assert name?.size() >3
}
您还可以看到Groovy的“强大的断言”语句提供的漂亮的输出:被断言的每个子表达式的值的图形视图。
Elvis操作符提供默认值
Elvis操作符是一种特殊的三元运算符的快捷方式,用于方便的使用默认值。
我们经常要这样写代码:
def result = name !=null ? name :"Unknown"
由于Groovy的特性,空检查name!=null可以简化成name。
再进一步,因为你总是返回name,与其在这个三元表达式中重复name两次,不如我们以某种方式删除问号和冒号之间的部分,通过使用Elvis操作符,上述代码变成:
def result = name ?:"Unknown"
捕获任何异常
如果你真的不关心你的try代码块内抛出的异常,你可以简单地捕捉所有的异常并忽略其类型。但是不是像下面一样捕捉throwables:
try {// ...}catch (Throwable t) {// something bad happens}
而是捕捉所有东西('任何'或'所有',或任何让你觉得这是一切的东西):
try {// ...}catch (any) {// something bad happens}
关于强弱类型的建议
最后我会以如何使用可选的类型结束本文。Groovy中你可以自己决定是否使用显式的强类型,或者使用“def”。
我有一个相当简单的经验法则:每当你写的代码会被其他人当作一个公共API使用,你应该总是使用强类型,它有助于增强规约,避免可能的传递错误类型参数,提供更好的文档,也有利于IDE的代码完成;如果代码仅是供自己使用,如私有方法,或当IDE可以很容易地推断出的类型,那么你可以自由决定。
相关推荐
许多 Java 开发人员非常喜欢 Groovy 代码和 Java 代码的相似性。从学习的角度看,如果知道如何编写 Java 代码,那就已经了解 Groovy 了。Groovy 和 Java 语言的主要区别是:完成同样的任务所需的 Groovy 代码比 Java...
《Groovy in Action》是Groovy编程的综合指南,它向Java开发者介绍了Groovy提供的新的动态特性。为了呈现《Groovy in Action》,Manning再次从源头工作,与包括Groovy项目团队成员和经理在内的专家作者团队合作。其结果...
java 动态脚本语言 精通 Groovy
Groovy 调用 Java 类groovy 调用 Java class 十分方便,只需要在类前导入该 Java 类,在 Groovy 代码中就可以无缝使用该
Groovy作可以运行为在JVM上的动态语言,为java开发者带来另一片天地
资源名称:Java脚本编程:语言框架与模式内容简介:《Sun公司核心技术丛书·Java脚本编程语言、框架与模式》讲解了脚本语言的基本概念和使用方法,概括了Java开发人员可以使用的解决方案,并探讨了在Java应用程序中...
如果你已经具备一定的Java基础,想快速学习一门脚本语言,Groovy是个自然的过渡选择。无论编码习惯还是风格,Groovy是最接近Java的。通过Groovy1.1的annotation支持,你能够在Groovy里很自然地使用Seam,Hibernate,...
Making Java Groovy is a practical handbook for developers who want to blend Groovy into their day to day work with Java It starts by introducing the key differences between Java and Groovy and how you...
本书是有关Groovy的第一本正式出版物,作者KennethBarclay和JohnSavage介绍了Groovy开发的所有主要领域,并解释了这种创新性的编程语言给Java平台赋予的动态特性。阅读本书只要求具备Java编程的一般性知识。不管你...
JGSK, Java,Groovy,Scala,Kotlin 四种语言的特点对比
Groovy环境搭建教程中的例子工程,纯Java、纯Groovy以及Java+Groovy混合 教程参考:http://blog.csdn.net/rcom10002/archive/2011/06/26/6568557.aspx
AndroidDemoIn4Languages, 在Android开发中,比较 Java Groovy Scala Kotlin 中文版 日本語 AndroidDemoIn4Languages为了了解Android开发的更好语言,用 Java 。Groovy 。Scala 和Kotlin编写了一个简单的Android应用...
, 本书是有关Groovy的第一本正式出版物,作者Kenneth Barclay和John Savage介绍了Groovy开发的所有主要领域,并解释了这种创新性的编程语言给Java平台赋予的动态特性。阅读本书只要求具备Java编程的一般性知识。不管...
Java 开发 2_0 通过 CouchDB 和 Groovy 的 RESTClient 实现 REST
Groovy 是用于Java虚拟机的一种敏捷的动态语言,它结合了Python、Ruby和Smalltalk的许多强大的特性。它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言。使用该种语言不必编写过多...
Making Java Groovy
[Pragmatic Bookshelf] Groovy 编程 (英文版) [Pragmatic Bookshelf] Programming Groovy Dynamic Productivity for the Java Developer (E-Book) ☆ 出版信息:☆ [作者信息] Venkat Subramaniam [出版机构] ...
Java调用Groovy,实时动态加载数据库groovy脚本,java读取mongoDB的groovy脚本,加载实时运行,热部署
概述主要介绍Java、spring与groovy结合使用,高清英文版本
Java中使用Groovy的三种方式,详细见我的博客。