概述
groovy与java的class的共性:从分类来看,分为普通类、匿名类、抽象类、接口、匿名内部类;从自定义来看,标识符class、访问限定符(public、protected 、private、static),属性与方法的定义等都非常类似。
访问权限的区别:groovy的class将java中默认访问权限与public等效,即没有指定访问限定符都被当成public来使用。
直接引用属性的区别:groovy的class使用直接引用属性来替代get/set方法,注意直接引用属性实际调用的是对应的get/set方法。
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; } }
构造方法
对于提供了构造方法的类,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) }
上述三种实例化方式对应的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); }
由上可知,第一种是直接调用构造方法;第二种则是使用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; } ... ... }
对于没有提供了构造方法的类,可以使用如下的几种方式:
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) }
注意:没有构造方法是不能使用前面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; }
总之,上面通过map来封装构造参数,不再限制构造方法的参数位置与数量,比较灵活。
方法
方法返回类型:指定返回类型或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); }
注意:在方法中没有提供return语句情况下,groovy会以最后被执行的那行代码为依据作为返回值,如上述例子。
与构造方法类似:通过以map作为参数可突破普通方法对参数的限制,如下代码,好处是使得方法更加的灵活通用,不需要提供多个重载方法:
def testMethod(Map args) { println("args:${args}") } testMethod(name: 'jake', age: 22, phone: 123456)
如果在调用方法时提供了默认参数,调用方法时可以不传递有默认值的参数,如下:
def testMethod(String name, int age = 12) { println("name=$name,age=$age") } testMethod('Jake')
实现的原理是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); }
不定参数定义有两种:一是与java一样(…)或使用对象数组,两种方式是等效的,如下例子:
def testVarargsArags(Object... args) { args.length } println(testVarargsArags(1, 2, 3)) def testAragsArray(Object[] args) { args.length } println(testAragsArray(1, 2, 3))
以下是编译过后的字节码:
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); }
字段与属性
Fields:在groovy中将类中的成员变量统称为Fields,组成格式如下:
访问限定符(public..) + 可选修饰符(static…) + 类型(可省略)+ 变量名称注意:groovy建议Fields的类型最好要明确指定,以防止某些应用场景依赖具体类型。
properties:是groovy从Fields中那些被限定private的Fields,并提供了getters/setters方法的统称。
注意:properties是不用提供get/set方法的,因为编译器会自动生成;可以直接使用properties访问对应的get/set方法;声明为final的属性,不会生成对应的set方法。
Groovy直接属性访问不仅限于properties,凡是提供了get/set方法的类都支持,如下javabean示例:
class JavaBean { String getId() { return '111' } } def testProperties() { def bean = new JavaBean() println(bean.id) }
注解
与java的注解类似,groovy注解定义格式:
@interface 注解名 { 类型 成员方法名() 默认值 }
类型被限制在:基本类型、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() }
注意:注解的成员方法是没有方法体,默认值是非必需的。
作用对象:与java注解类似,可通过Target注解来指定作用范围,如下示例:
@Target([ElementType.METHOD]) @interface AppAnnotation { String value() default 'normal' }
@AppAnnotation('test') void testAnnotation() { def i = 1 }
Retention注解:与java一样,不再赘述。
闭包注解:注解支持闭包作为参数,这极大的扩充了注解的应用场景。如下示例:
@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()
上述run方法依据配置参数params ,通过闭包调用将task类中的requiresJDK7AndWindows过滤掉,最终只有alwaysExecuted与supportedOnlyInJDK6能执行,执行的顺序依赖于declaredMethods返回的方法数组。
Meta-annotations:作用将一个注解替代多个注解,以减少编写注解数目,如下例子:
@interface Service {} @interface Transactional {} @Service @Transactional class MyTransactionalService { }
使用Meta-annotations:
@Service @Transactional @AnnotationCollector @interface TransactionalService {} @TransactionalService class MyTransactionalService { }
注意:Meta-annotations是groovy独有特性,在编译成字节码时会将Meta-annotations替换成Meta-annotations中声明的注解
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 {}