Groovy

2 篇文章 0 订阅

图谱:Spark<-Scala<-Gradle<-Groovy

Before you learn, setup your IDE well!!! IDEA is recommanded.

1. Groovy基于JVM;

2.如果把java文件的后缀改成.groovy,能够编译通过并运行。但是由于groovy是动态语言,很多行为可能会不一致。尤其涉及继承、多态的时候。如果调用方使用的是父类,则,java会找一个最上层的基类实现,但是groovy则会使用实际的对象中的方法。例如下面的代码中,如果是java,则在collection.remove中,实际调用的是Collection的直接子类AbstractCollection的remove方法,尝试将0转成Object去删除,但是Collection中没有Integer(0)这个对象,所以没有删除。Java并不会把0当做index。而对于groovy,即使使用的是collection.remove,groovy也会使用实际对象的类型中的remove,也就是ArrayList的remove,把0当做index。执行这段代码后,java会剩一个元素,groovy集合为空。

ArrayList<String> list=new ArrayList<>();
Collection<String> collection=list;
list.add("Jim");
list.add("Tom");
collection.remove(0);
list.remove(0);

3. groovy声明对象时,可以指定类型,但是和java不同,groovy并不在静态时检查这个类型,而是在运行时尝试做强制类型转换。因此是伪静态类型。groovy可以使用注解来执行更严格的编译时检查:

@groovy.transform.TypeChecked
def shout(String str){
    str.notExistMethod();
}

4. 动态语言没有编译时类型检查,甚至连方法、属性是否存在也不能确定,也不能帮助发现一些拼写错误。为了保障程序的功能能够正常实现,应该采用完整的ut来验证功能。动态语言对ut的要求更加严格。

5. 可以使用@CompileStatic让groovy执行静态编译,但是会失去元编程的能力,这主要是出于性能考虑。groovy动态编译可能导致损失10%的性能,但是静态编译可以产生和java相媲美的性能。

6. 闭包

def each(n, block){
    for(int i=0;i<n;++i){
        block(i)
    }
}

each(10, { it -> println it }) <=> each(10, { println it }) <=> each(10){ println it }

def examine(closure){
    println closure.maximumNumberOfParameters
}
examine(){} // closure.maximumNumberOfParameters = 1
examine(){it} // closure.maximumNumberOfParameters = 1
examine(){->} // closure.maximumNumberOfParameters = 0

7. 科里化(咖喱化,哈哈)

add3 = { x, y, z = x + y + z }
add2 = add3.curry(1)
add = add2.curry(2)
addr = add3.rcurry(5) // <=> add3(x, y, 5)
addn = add3.ncurry(1, 9) // <=> add3(x, 9, z)
add3.call(4, 5, 6) <=> add3(4, 5, 6)
add(3) //6

8. GDK

Groovy 给java.lang.Object增加了一些方法。还有更多,例如use,mixin 等都在 DefaultGroovyMethods 中定义。

obj.dump()
obj.inspect();

list=[]; list.add(3); list.add(4) <=> list=[]; list.with(add(3);add(4));

9. 一个简单的DSL, 放在Counter.groovy中可以直接运行。以get set开头的方法是定义了类的属性访问器。

value = 10

def getClear() {
    value = 0
}

def getTotal() {
    println "Total count is ${value}"
}

def add(int n) {
    value += n
}

clear
add 3
total
add 4
total
clear
total

10. this, delegate和owner

this 指闭包本身;owner指定义闭包的外层闭包;默认delegate和owner相等,但是delegate可以被修改。

List.metaClass.myeach={delegate.each{println it}}

11. MOP(MetaObject Protocol) 和元编程

Groovy对象分为三种:

POJO普通java对象

POGO使用Groovy编写的对象,扩展了java.lang.Object 对象,同时实现了groovy.lang.GroovyObject接口。一旦一个类被加载到JVM中,我们就不能修改它的元对象Class了,但是我们可以通过setMetaClass修改它的MetaClass,造成一种对象运行时类被修改了的感觉。

package groovy.lang;
public interface GroovyObject {
    Object invokeMethod(String var1, Object var2);
    Object getProperty(String var1);
    void setProperty(String var1, Object var2);
    MetaClass getMetaClass();
    void setMetaClass(MetaClass var1);
}

Groovy拦截器 是扩展了GroovyInterceptable接口的Groovy对象。不管对象存在的还是不存在的方法调用,都会被它的invokeMethod拦截。对于一般的groovy对象时只有当方法不存在时,才会调用的invokeMethod方法,除非在对象的MetaClass上实现了InvokeMethod方法

package groovy.lang;
public interface GroovyInterceptable extends GroovyObject {
}

Groovy对调用方法的路由:

1)如果是POJO对象,先从MetaClassRegistry中取出MetaClass,并将方法代理给它。因此在MetaClass上定义的任何方法都先于POJO自己定义的方法

2)如果是POGO对象,且实现了GroovyInterceptable接口,所有的调用都会被路由到invokeMethod方法,在这个拦截器内,调用正确的方法,使得AOP成为可能。

3)如果是POGO对象,没有实现GroovyInterceptable,则会先查找MetaClass中定义的方法,如果没有找到,就调用POGO本身的方法。如果POGO没有这样的方法,会查找POGO的属性或者字段。如果属性或者字段是Closure类型,则调用它以代替方法调用。接下来会尝试调用methodMissing方法,否则调用invokeMethod方法。invokeMethod的默认实现是抛出MethodMissingException。其中invokeMethod和methodMissing必须有严格的prototype声明。一下还给出了调用默认实现的一个invokeMethod的实现。

def invokeMethod(String method, Object args){
    metaClass.getMetaMethod(method, args).invoke(this, args)
    //metaClass.invokeMethod(this, method, args);
}

查看某个方法能否被相应

String.metaClass.respondsTo(str,"compareTo","test") // true

12. MOP 方法注入的三种方式

1) 分类。背后的原理是调用GroovyCategorySupport.use(categoryClass, closure)方法,创建临时作用域,将新方法附在目标对象的MetaClass上。一旦闭包结束,则丢弃所有加入的新方法。也可以使用@Category注解来实现。

class StringUtil {
    def static dup(str) {
        str + str
    }
}

use(StringUtil) {
    println "hello,".dup()
}

2) ExpandoMetaClass

向类的MetaClass添加新方法,这样会将累的MetaClass从MetaClassImpl变成ExpandoMetaClass。可以通过<< 添加新的构造函数。改变某个对象的MetaClass而不是整个类。这个像极了js

def jack = new Person()
def emc = new ExpandoMetaClass(Person)
emc.sing = {-> "Sing..."}
emc.initialize()
jack.metaClass = emc
println jack.sing()
jack.metaClass=null //去除添加在metaClass上的方法

3) Mixin

class Friend{
    def sayHello(){
        println "Hello, $name!"
    }
}
/**************************************/
@Mixin(Friend)
class Person{
    def name
    Person(){
        name="Jim"
    }
}

new Person().sayHello()
/**************************************/
Person.mix Friend
new Person().sayHello()
/**************************************/
def person = new Person()
person.metaClass.mix Friend
person.sayHello()

13. 动态创建类:Expando,这个仍然像极了js

def car = new Expando(mile: 300, turn:{dir -> println "turning $dir"})
car.color = "green"

14. 在java/groovy中使用java类,groovy类和groovy脚本

1) 运行groovy脚本

groovy script.groogy //run directly

groovyc script.groovy // compile first, generate script.class
groovy -cp . script // run the groovy class

2) groovy中使用java

groovyc -j Animal.java useAnimal.groovy // outputs Animal.class, useAnimal.class
groovy -cp . useAnimal

也可以使用javac自行对java文件进行编译生成class文件,将生成的class文件加入到groovy的classpath中即可。毕竟groovy的类中包含POJO,POGO,Interceptable,groovy可以正确处理POJO。

3)java中使用groovy静态方法

// Function.groovy
class Function{
    def apply(v,closure){
        closure(v)
    }
}
//Main.java
public class Main{
    public static void main(String[] args){
        System.out.println(new Function().apply(3, new Object(){
            public int call(int v){
                return v * 2;
            }
        }));
    }
}
groovyc -j Main.java Function.groovy // compile
java -cp groovy-3.0.0-alpha-3/lib/groovy-3.0.0-alpha-3.jar: Main // run

4) Java中使用groovy的动态方法

只需要将产生的对象赋值给 groovy.lang.GroovyObject,然后调用该对象的invokeMethod方法即可。

5) Groovy中使用groovy脚本 需要使用GroovyShell,使用Binding传递参数,使用run来传递命令行参数

6) Java 中使用Groovy脚本 需要使用javax.script.ScriptEngineManager和ScriptEngine

 

Questions

1. Groovy中callsite的概念。脚本会被转化成Call site,机制是什么?

2. Groovy可以修改metaClass,这个是如何存储的?这部分可能需要明白JVM的编译和反编译机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值