8. groovy的面向对象
groovy来自java,当然是含有面向对象的基因。
8.1 属性和本地变量
groovy有private,protected,public等访问修饰符,但据groovy 2.0.7测试的结果,这些修饰符都是没有起作用,都是public的访问权限,这似乎是一个bug,但是尚未修复,http://jira.codehaus.org/browse/GROOVY-1875
另外groovy bean会生成getter/setter,当然你也可以覆盖set,可以通过点操作符来调用,也可以通过['propertyName']动态的来访问。
package org.jamee.hello.groovy.bean
class BeanAccessor {
def dftField;
private priField;
protected protField;
def AnotherBean ab;
}
package org.jamee.hello.groovy.bean
class AnotherBean {
def name;
void setName(def name) {
this.name = 'in setName ' + name;
}
}
package org.jamee.hello.groovy.main
import org.jamee.hello.groovy.bean.AnotherBean;
import org.jamee.hello.groovy.bean.BeanAccessor;
class GroovyTester {
static main(args) {
def ba = new BeanAccessor()
ba.dftField = 'default value'
ba.priField = 'private value'
println ba.dftField
println ba['dftField']
println ba.priField
ba.ab = new AnotherBean()
ba.ab.name = 'ab\'s name'
println ba.ab.name;
ba.protField = 'protected value'
println ba['protField']
}
}
输出:
写道
default value
default value
private value
in setName ab's name
protected value
8.2 默认参数,变长参数和命名参数
groovy借用了其他脚本语言的特性,支持默认参数和命名参数,虽然看起来不是那么友好。
class Greety {
def sayHello(person, message="good day") {
return "Hi, $person, $message"
}
def sayHellos(message, person, Object[] persons) { // like def SayHellos(message, person, ...otherPersons)
return persons.inject(message +" $person,") {words, i -> words += " $i,"}
}
def sayHello(Map namedParams) {
return "${namedParams.message}, ${namedParams.person}"
}
}
def greety = new Greety()
println greety.sayHello('jhone') // output: Hi, jhone, good day
println greety.sayHello('allen', 'how are you') // output: Hi, allen, how are you
println greety.sayHellos('good day', 'bill', 'clark', 'danny') // called like var args, output:good day bill, clark, danny,
println greety.sayHello(message:'good day', person:'eval') // output: good day, eval
8.3 方法调用
groovy的调用方法可以用字符串或字符串变量的方式,即更加方便的使用java的反射。
另外也对令人头疼的NullPointerException提供了更方便的检查方式。
class Foo {
def callme() {
return 'foo called'
}
}
def foo = new Foo()
println foo.callme() // output: foo called
println foo.'callme'() // output: foo called
def mn = 'callme'
println foo."$mn"() // output: foo called
def map = [a:[b:[c:1]]]
println map.a.b.c
// println map.a.x.c // NPE throw
println map?.a?.x?.c // protected
8.4 构造器
groovy提供了三种构造实例的方式:java传统的方式,as转换构造,和隐式构造。
另外groovy还为构造函数提供了命名参数,这样对于属性很多的类,使用就很方便了,还记得java的builder模式吧,groovy天生支持了。
class Name {
def firstName;
def lastName;
Name(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
def String toString() {
return this.firstName + ' ' + this.lastName
}
}
def n0 = new Name('alex', 'bell')
def n1 = ['bob','jons'] as Name
assert n1 instanceof Name
Name n2 = ['clark', 'lee']
assert n2 instanceof Name
class Person {
Name name
def age
def gender
def email
}
new Person()
p1 = new Person(name : n0)
println p1.name // output: alex bell
p2 = new Person(name : n1, email : '[email protected]')
println p2.email // output: [email protected]
8.5 类,包
之前提到过,groovy是java的另一种表现形式,任何一个groovy的脚本都是编译成类的,如果一个脚本不含class,groovy会默认将整个脚本编译成一个类,并加入main方法。
groovy的包的组织形式和java一样,但groovy提供了一个我期待很久的特性,可以给一个类引用一个别名,类似于C中的typedef,这样在一个class中引用两个同名的class就不必要写很长的包名了。
import com.company.util.StringUtil
import com.myCompany.util.StringUtil as MyStringUtil
8.6 GPath,Spread,Use
GPath
可以让你用属性访问短方法调用使得你的代码更加紧凑,例如:
invoices.items.grep{ it.total() > 7000 }.product.name
对应的Java代码:
private static List getProductNamesWithItemTotal(Invoice[]
invoices) {
List result = new LinkedList();
for (int i = 0; i < invoices.length; i++) {
List items = invoices[i].getItems();
for (Iterator iter = items.iterator(); iter.hasNext();) {
LineItem lineItem = (LineItem) iter.next();
if (lineItem.total() > 7000){
result.add(lineItem.getProduct().getName());
}
}
}
return result;
}
Spread操作符(*)
可以将你的集合展开,以方便调用,python也提供了类似的操作。
list = [1,2,3]
range = 1..3
def connect(x, y, z) {
return x + y + z
}
assert 6 == connect(*list)
assert 6 == connect(*range)
Use操作符
class StringCaculationCategory {
static String plus(String first, String second) {
Integer.parseInt(first) + Integer.parseInt(second)
}
}
use(StringCaculationCategory) {
println '1' + '2'
}