7.1 定义类和脚本
7.1.1 定义属性和本地变量
变量的声明
属性和本地变量在使用之前必须进行声明(除了脚本之外),这有助于实施作用域规则和防止程序员偶然的拼写错误。
脚本允许使用没有声明的变量,在这种情况下变量被假定从脚本的 binding 属性获取,如果在 binding 中没有发现相应的变量,那么把变量增加到binding中,binding是一个数据存储器,它能把变量在脚本调用者和脚本之间进行传递。
缺省属性范围在 groovy 有特殊的意思,在没有指定范围修饰符的属性声明的时候,groovy会根据需要生成相应的访问方法(getter 方法和 setter 方法)。
定义变量的类型是可选的,
当没有类型和修饰符时,必须使用 def 来作为替换,实际上用 def 来表明属性或者变量是没有类型的(尽管在内部将被声明为 Object 类型)
指定了类型的引用必须符合给定的类型
引用属性和取消对属性的引用
除了可以通过 obj.fieldName 来引用属性之外,也可以通过下标操作符来引用属性
重写下标操作符:
Object get (String name)
void set (String name, Object value)
重写 get 方法意味着重写了 dot-fieldName 操作符,重写 set 方法意味着重写了 field assignment 操作符。
语句 x.z.y=something 这个语句执行的结果是什么?这与 getX().getY().setZ(somethine)等价
7.1.2 方法和参数
通常能使用 java 修饰符;返回类型的声明是可选的;如果没有修饰符或者返回类型,那么使用 def 关键字来填空白,当使用 def关键字的时候,方法的返回类型被认为是没有类型(尽管没有返回类型,这时与 void 方法等价),在这种情况下,groovy 将方法的返回类型定义为 java.lang.Object,
缺省的方法访问范围为 public
。
方法分派
package groovyInAction.seven
class Example7_5DeclaringMethods {
static void main(String[] args) {
def some = new Example7_5DeclaringMethods()
some.publicVoidMethod()
assert 'hi' == some.publicUntypeMethod()
assert 'ho' == some.publicTypeMethod()
combineMethod()
}
void publicVoidMethod(){}
def publicUntypeMethod(){
return 'hi'
}
def publicTypeMethod(){
return 'ho'
}
protected static final void combineMethod(){}
}
main 方法 args 目前的类型隐式为 java.lang.Object
显式声明参数的类型是可选的,当声明类型被忽略的时候,groovy 使用 Object 作为类型,可以顺序的使用多个参数,通过逗号进行分隔
package groovyInAction.seven
class Example7_6DeclaringParameterLists {
static void main(args){
assert 'untyped' == method(1)
assert 'typed' == method('whatever')
assert 'two args' == method(1,2)
}
static method(arg){
return 'untyped'
}
static method(String arg){//方法重载
return 'typed'
}
static method(arg1,Number arg2){
return 'two args'
}
}
所有方法调用都与方法参数的位置有关,这意味做每一个参数已经根据它参数列表中的位置被决定了。对于复杂的脚本有一些痛苦的缺点:
你必须记住真实的参数的顺序,当参数列表比较长时这增加了难度;
对于非传统的脚本使用,如果根据不同的信息调用方法是有意义,那么需要构建很多不同的方法来处理这些差异,这很快就变得笨重起来并且导致方法的难以维护,特别是当有些参数是可选的时候,如果许多可选的参数具有相同的类型,这会变得特别困难。
注意:每当谈论到命名的参数的时候,我们的意思是map的key在方法中被用作参数被调用,从一个程序员的视角看,这看起来就像原生的命名参数的支持,但其实不是这样的,这种技巧是必须的,因为JVM不支持把参数的名称保存在字节码中。
package groovyInAction.seven
class Example7_7AdvancedParameterUsages {
static main(args){
def summer = new Summer()
assert 2 == summer.sumWithDefaults(1, 1)
assert 3 == summer.sumWithDefaults(1, 1,1)
assert 2 == summer.sumWithList([1,1])
assert 3 == summer.sumWithList([1,1,1])
assert 2 == summer.sumWithOptionals(1,1)
assert 3 == summer.sumWithOptionals(1,1,1)
assert 2 == summer.sumNamed(a:1,b:1)
assert 3 == summer.sumNamed(a:1,b:1,c:1)
assert 1 == summer.sumNamed(c:1)
}
}
class Summer{
def sumWithDefaults(a, b, c=0){ //Explicit arguments and a default value
return a + b + c
}
def sumWithList(List args){ //Define arguments as a list
return args.inject(0) {sum,i -> sum +=i}
}
def sumWithOptionals(a,b,Object[] optionals){//Optional arguments as an array
return a + b + sumWithList(optionals.toList())
}
def sumNamed(Map args){//Define arguments as a map
['a','b','c'].each{ args.get(it,0)}
return args.a + args.b + args.c
}
}
注意:实现可变长度的参数列表的第二种方式是,你可以覆盖groovy的分派方法invokeMethod(name,params[]),这是每个GroovyObject对象都提供的方法。
高级命名
objectReferenct.'my.method-Name'
这个特性的目的是支持把调用方法的名称变成函数的一部分,通常情况下不直接使用这个特性,但是groovy的其他部分将使用到这个特性。
obj."${var}"()
7.1.3 安全的引用符号(?.)
package groovyInAction.seven
class Example7_8ProtectingFromNullPointerExceptionsUsingTheOperator {
static main(args){
def map = [a:[b:[c:1]]]
assert map.a.b.c == 1
if(map && map.a && map.a.x){//protect with if:short-circuit evaluation
assert map.a.x.c == null
}
try{//Protect with try/catch
assert map.a.x.c == null
}catch(NullPointerException npe){
}
assert map?.a?.x?.c == null //Safe dereferencing
}
}