Groovy语法之类

概述
  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
    @Transactional
    class MyTransactionalService {
    }

    使用Meta-annotations:

    @Service
    @Transactional
    @AnnotationCollector
    @interface TransactionalService {}
    
    @TransactionalService
    class 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 {}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值