Groovy语法之类

来源:互联网 发布:守望先锋左上角6个数据 编辑:程序博客网 时间:2024/06/10 20:14

概述

  1. groovy与java的class的共性:从分类来看,分为普通类、匿名类、抽象类、接口、匿名内部类;从自定义来看,标识符class、访问限定符(public、protected 、private、static),属性与方法的定义等都非常类似。

  2. 访问权限的区别:groovy的class将java中默认访问权限与public等效,即没有指定访问限定符都被当成public来使用。

  3. 直接引用属性的区别:groovy的class使用直接引用属性来替代get/set方法,注意直接引用属性实际调用的是对应的get/set方法。

  4. get/set方法生成的区别:groovy的class会自动生成get/set方法,不需要重复提供。如下代码:

    class Dog {    String name}def dog = new Dog()dog.getName()

    Dog编译之后的class:

    public class Dog implements GroovyObject {    private String name;    public Dog() {        CallSite[] var1 = $getCallSiteArray();        MetaClass var2 = this.$getStaticMetaClass();        this.metaClass = var2;    }    public String getName() {        return this.name;    }    public void setName(String var1) {        this.name = var1;    }}

构造方法

  1. 对于提供了构造方法的类,groovy有如下三种方式来实例化该对象:

    class Person {    String name    int age    Person(String name, int age) {        this.name = name        this.age = age    }    @Override    String toString() {        return "name=$name, age=$age"    }}
    void testConstructors() {    def obj1 = new Person('jake', 22)    println(obj1)    def obj2 = ['jake', 22] as Person    println(obj2)    Person obj3 = ['jake', 22]    println(obj3)}
  2. 上述三种实例化方式对应的class如下:

    public void testConstructors() {    CallSite[] var1 = $getCallSiteArray();    Object obj1 = var1[2].callConstructor(Person.class, "jake", Integer.valueOf(22));    var1[3].callCurrent(this, obj1);    Object obj2 = (Person)ScriptBytecodeAdapter.asType(ScriptBytecodeAdapter.createList(new Object[]{"jake", Integer.valueOf(22)}), Person.class);    var1[4].callCurrent(this, obj2);    Person obj3 = (Person)ScriptBytecodeAdapter.castToType(ScriptBytecodeAdapter.createList(new Object[]{"jake", Integer.valueOf(22)}), Person.class);    var1[5].callCurrent(this, obj3);}
  3. 由上可知,第一种是直接调用构造方法;第二种则是使用as操作符来完成对象的创建,原理是使用as操作符完成对象实例化;第三种则是直接强转成目标对象的示例,原理是使用list来保存构造方法的参数,然后作为目标对象的参数,最终使用new构造示例,如下:

    private static Object continueCastOnSAM(Object object, Class type) {    ... ...    Object[] args = null;    if (object instanceof Collection) {        // let's try invoke the constructor with the list as arguments        // such as for creating a Dimension, Point, Color etc.        Collection collection = (Collection) object;        args = collection.toArray();    } else if (object instanceof Object[]) {        args = (Object[]) object;    } else if (object instanceof Map) {        // emulate named params constructor        args = new Object[1];        args[0] = object;    } ... ... }
  4. 对于没有提供了构造方法的类,可以使用如下的几种方式:

    class Cat {    String name    int age}void testNamedArgumentConstructors() {    def cat1 = new Cat()    println(cat1)    def cat2 = new Cat(name: 'Marie')    println(cat2)    def cat3 = new Cat(age: 1)    println(cat3)    def cat4 = new Cat(name: 'Marie', age: 2)    println(cat4)    //def cat5 = ['jake', 22] as Cat    //println(cat5)    //def cat6 = ['jake', 22]    //println(cat6)}
  5. 注意:没有构造方法是不能使用前面as操作符或强转来实例化的,再看编译之后的字节码:

    public void testNamedArgumentConstructors() {    CallSite[] var1 = $getCallSiteArray();    Object cat1 = var1[2].callConstructor(Cat.class);    var1[3].callCurrent(this, cat1);    Object cat2 = var1[4].callConstructor(Cat.class, ScriptBytecodeAdapter.createMap(new Object[]{"name", "Marie"}));    var1[5].callCurrent(this, cat2);    Object cat3 = var1[6].callConstructor(Cat.class, ScriptBytecodeAdapter.createMap(new Object[]{"age", Integer.valueOf(1)}));    var1[7].callCurrent(this, cat3);    Object cat4 = var1[8].callConstructor(Cat.class, ScriptBytecodeAdapter.createMap(new Object[]{"name", "Marie", "age", Integer.valueOf(2)}));    var1[9].callCurrent(this, cat4);}
    public static Map createMap(Object[] values) {    Map answer = new LinkedHashMap(values.length / 2);    int i = 0;    while (i < values.length - 1) {        if ((values[i] instanceof SpreadMap) && (values[i + 1] instanceof Map)) {            Map smap = (Map) values[i + 1];            Iterator iter = smap.keySet().iterator();            for (; iter.hasNext();) {                Object key = iter.next();                answer.put(key, smap.get(key));            }            i += 2;        } else {            answer.put(values[i++], values[i++]);        }    }    return answer;}
  6. 总之,上面通过map来封装构造参数,不再限制构造方法的参数位置与数量,比较灵活。

方法

  1. 方法返回类型:指定返回类型或def关键字,编译器会当成object类型返回。如下代码:

    def testMethod() {    a = 1}
    public Object testMethod() {        CallSite[] var1 = $getCallSiteArray();        byte var2 = 1;        ScriptBytecodeAdapter.setGroovyObjectProperty(Integer.valueOf(var2), Main.class, this, (String)"a");        return Integer.valueOf(var2);}
  2. 注意:在方法中没有提供return语句情况下,groovy会以最后被执行的那行代码为依据作为返回值,如上述例子。

  3. 与构造方法类似:通过以map作为参数可突破普通方法对参数的限制,如下代码,好处是使得方法更加的灵活通用,不需要提供多个重载方法:

    def testMethod(Map args) {    println("args:${args}")}testMethod(name: 'jake', age: 22, phone: 123456)
  4. 如果在调用方法时提供了默认参数,调用方法时可以不传递有默认值的参数,如下:

    def testMethod(String name, int age = 12) {    println("name=$name,age=$age")}testMethod('Jake')
  5. 实现的原理是groovy编译代码之后会自动生成对应的重载方法,如下:

    public Object testMethod(String name, int age) {    CallSite[] var3 = $getCallSiteArray();    return var3[2].callCurrent(this, new GStringImpl(new Object[]{name, age}, new String[]{"name=", ",age=", ""}));}public Object testMethod(String name) {    CallSite[] var2 = $getCallSiteArray();    return !__$stMC && !BytecodeInterface8.disabledStandardMetaClass() ? this.testMethod(name, 12) : this.testMethod(name, 12);}
  6. 不定参数定义有两种:一是与java一样(…)或使用对象数组,两种方式是等效的,如下例子:

    def testVarargsArags(Object... args) {    args.length}println(testVarargsArags(1, 2, 3))def testAragsArray(Object[] args) {    args.length}println(testAragsArray(1, 2, 3))
  7. 以下是编译过后的字节码:

    public Object testVarargsArags(Object... args) {    CallSite[] var2 = $getCallSiteArray();    return var2[5].callGetProperty(args);}public Object testAragsArray(Object... args) {    CallSite[] var2 = $getCallSiteArray();    return var2[6].callGetProperty(args);}

字段与属性

  1. Fields:在groovy中将类中的成员变量统称为Fields,组成格式如下:
    访问限定符(public..) + 可选修饰符(static…) + 类型(可省略)+ 变量名称

  2. 注意:groovy建议Fields的类型最好要明确指定,以防止某些应用场景依赖具体类型。

  3. properties:是groovy从Fields中那些被限定private的Fields,并提供了getters/setters方法的统称。

  4. 注意:properties是不用提供get/set方法的,因为编译器会自动生成;可以直接使用properties访问对应的get/set方法;声明为final的属性,不会生成对应的set方法。

  5. Groovy直接属性访问不仅限于properties,凡是提供了get/set方法的类都支持,如下javabean示例:

    class JavaBean {    String getId() {        return '111'    }}def testProperties() {    def bean = new JavaBean()    println(bean.id)}

注解

  1. 与java的注解类似,groovy注解定义格式:

    @interface 注解名 {    类型 成员方法名() 默认值}
  2. 类型被限制在:基本类型、String、Class、枚举、注解类型或这些类型的数组类型,如下示例:

    @interface SomeAnnotation {    String value()}@interface SomeAnnotation {    String value() default 'something'}@interface SomeAnnotation {    int step()}@interface SomeAnnotation {    Class appliesTo()}@interface SomeAnnotation {}@interface SomeAnnotations {    SomeAnnotation[] value()}enum DayOfWeek {    mon, tue, wed, thu, fri, sat, sun}@interface Scheduled {    DayOfWeek dayOfWeek()}
  3. 注意:注解的成员方法是没有方法体,默认值是非必需的。

  4. 作用对象:与java注解类似,可通过Target注解来指定作用范围,如下示例:

    @Target([ElementType.METHOD])@interface AppAnnotation {    String value() default 'normal'}
    @AppAnnotation('test')void testAnnotation() {    def i = 1}
  5. Retention注解:与java一样,不再赘述。

  6. 闭包注解:注解支持闭包作为参数,这极大的扩充了注解的应用场景。如下示例:

    @Retention(RetentionPolicy.RUNTIME)@interface OnlyIf {    Class value()}
    class Tasks {    Set result = []    void alwaysExecuted() {        result << 1    }    @OnlyIf({ jdk >= 6 })    void supportedOnlyInJDK6() {        result << 'JDK 6'    }    @OnlyIf({ jdk >= 7 && windows })    void requiresJDK7AndWindows() {        result << 'JDK 7 Windows'    }}
    class Runner {    static <T> T run(Class<T> taskClass) {        def tasks = taskClass.newInstance()        def params = [jdk:6, windows: false]        tasks.class.declaredMethods.each { m ->            if (Modifier.isPublic(m.modifiers) && m.parameterTypes.length == 0) {                def onlyIf = m.getAnnotation(OnlyIf)                if (onlyIf) {                    Closure cl = onlyIf.value().newInstance(tasks,tasks)                    cl.delegate = params                    if (cl()) {                        m.invoke(tasks)                    }                } else {                    m.invoke(tasks)                }            }        }        tasks    }}
    void testClosureAnnotation() {    def tasks = Runner.run(Tasks)    println(tasks.result)//[JDK 6, 1]或[1, JDK 6]}testClosureAnnotation()
  7. 上述run方法依据配置参数params ,通过闭包调用将task类中的requiresJDK7AndWindows过滤掉,最终只有alwaysExecuted与supportedOnlyInJDK6能执行,执行的顺序依赖于declaredMethods返回的方法数组。

  8. Meta-annotations:作用将一个注解替代多个注解,以减少编写注解数目,如下例子:

    @interface Service {}@interface Transactional {}@Service@Transactionalclass MyTransactionalService {}

    使用Meta-annotations:

    @Service@Transactional@AnnotationCollector@interface TransactionalService {}@TransactionalServiceclass MyTransactionalService {}
  9. 注意:Meta-annotations是groovy独有特性,在编译成字节码时会将Meta-annotations替换成Meta-annotations中声明的注解

  10. Meta-annotations参数传递两种方式:一是在定义Meta-annotations传递,或在使用Meta-annotations时传递参数。如下例子:

    @interface Service {    int type()}@interface Transactional {    boolean status()}@Service(type = 1)@Transactional@AnnotationCollector@interface TransactionalService {}@TransactionalService(status = true)class MyTransactionalService1 {}