Groovy编程风格和语言特性指南(针对Java开发人员)

原文 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支持99Java的语法,并且某些时候复制一些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)

当一个闭包是一个方法调用的最后一个参数,如在使用Groovyeach函数,你可以把闭包放在括号外,甚至可以省略括号:

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中,一个gettersetter方法形成了我们所谓的属性”,并提供了一个捷径来访问和设置这些属性Java的调用方式不同,你可以使用一个类似字段访问的方式

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

resourceGroup.resourcePrototype.name == SERVER_TYPE_NAME 

resourcePrototype.setName("something")

resourcePrototype.name = "something"

Groovy编写bean,通常被称为POGOsPlain 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编译器生成一个私有字段相应gettersetter

当然,在Java中使用POGOsgettersetter确实存在,并可以照常使用。

尽管编译器创建通用的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的==其实是Groovyis()方法,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提供原生的语法来构造如listmap,正则表达式,范围值等数据结构。确保在你的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的switchC-风格的语言--通常只接受基本类型功能更强大。Groovyswitch语句接受几乎任何一种类型。

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.Listjava.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

所有对象都可以被转成”一个布尔值:一切nullvoid或empty都是false,如果不是,则计算结果为true

因此,if (name) {}写法完全可以替代if (name != null && name.length > 0) {}集合类也是如此。

于是可以在类似while()if(),三元运算符,Elvis操作符(见下文)等中运用此技巧

甚至可以自定义的Groovytrue,通过添加一个的布尔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可以很容易地推断出的类型,那么你可以自由决定

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值