Groovy语法基础

Groovy与Java

Groovy是一种与Java非常相似的脚本语言,编译器会将该脚本语言编译成class字节码文件,最终运行于Java虚拟机之上。

环境配置

  1. 前提是配置好JDK

  2. Groovy环境在类Unix上配置,只需以下的几行命令即可:

  3. 第一步下载sdkman,这是管理sdk的工具,命令如下:

    curl -s get.sdkman.io | bash  
  4. 读取并执行sdkman的初始化脚本,如下命令:

    source "$HOME/.sdkman/bin/sdkman-init.sh"
  5. 接着安装groovy的sdk,命令如下:

    sdk install groovy
  6. 最后,检验是否安装成功:

    groovy -version

    这里写图片描述

Groovy工具

  1. 安装完Groovy环境之后,输入groovy命令时,会有如下的工具:
    这里写图片描述

  2. groovyc:groovy编译器,类似于javac,将groovy脚本编译成class字节码文件

  3. groovy:用于运行groovy脚本

  4. groovyConsole:Groovy官方提供的一个简易IDE,如下图
    Groovy语法基础_第1张图片

  5. groovysh:groovy命令交互式的shell,类似python中的交互式环境。

  6. 另外,很多出名的IDE均已支持Groovy,本人使用的是IntelliJ IDEA。

引用标识符

  1. 注意,Groovy的语法与Java十分相似,这里只重点介绍与Java有区别的语法,相同的就不再赘述。

  2. 引用标识符:Groovy中对变量的引用方式是多样化的,Test.groovy源码如下:

    void testQuotedIdentifiers() {
        def map = [:]
    
        map.no_quote = 1
        map.'single quote' = 2
        map."double quote" = 3
        map.'''triple single quote''' = 4
        map."""triple double quote""" = 5
        map./slashy string/ = 6
        map.$/dollar slashy string/$ = 7
    
        def closureQuote = "value"
        map."closure quote ${closureQuote}" = 8
    
        println(map)
    }
    
    testQuotedIdentifiers()

    运行结果如下:

    [no_quote:1, single quote:2, double quote:3, triple single quote:4, triple double quote:5, slashy string:6, dollar slashy string:7, closure quote value:8]
  3. 上述代码先定义一个静态方法,然后在方法内定义了一个Map(本质上是java中的LinkedHashMap实例)。接着使用不同的引用操作符的方式往这个map中添加键值对,最后打印这个map实例,如下图:
    Groovy语法基础_第2张图片

  4. 紧接着来看Test.groovy编译之后的class代码:

    public class Test extends Script {
    public Test() {
        CallSite[] var1 = $getCallSiteArray();
    }
    
    public Test(Binding context) {
        CallSite[] var2 = $getCallSiteArray();
        super(context);
    }
    
    public static void main(String... args) {
        CallSite[] var1 = $getCallSiteArray();
        var1[0].call(InvokerHelper.class, Test.class, args);
    }
    
    public Object run() {
        CallSite[] var1 = $getCallSiteArray();
        if (!__$stMC && !BytecodeInterface8.disabledStandardMetaClass()) {
            this.testQuotedIdentifiers();
            return null;
        } else {
            return var1[1].callCurrent(this);
        }
    }
    
    public void testQuotedIdentifiers() {
        CallSite[] var1 = $getCallSiteArray();
        Object map = ScriptBytecodeAdapter.createMap(new Object[0]);
        byte var3 = 1;
        ScriptBytecodeAdapter.setProperty(Integer.valueOf(var3), (Class)null, map, (String)"no_quote");
        byte var4 = 2;
        ScriptBytecodeAdapter.setProperty(Integer.valueOf(var4), (Class)null, map, (String)"single quote");
        byte var5 = 3;
        ScriptBytecodeAdapter.setProperty(Integer.valueOf(var5), (Class)null, map, (String)"double quote");
        byte var6 = 4;
        ScriptBytecodeAdapter.setProperty(Integer.valueOf(var6), (Class)null, map, (String)"triple single quote");
        byte var7 = 5;
        ScriptBytecodeAdapter.setProperty(Integer.valueOf(var7), (Class)null, map, (String)"triple double quote");
        byte var8 = 6;
        ScriptBytecodeAdapter.setProperty(Integer.valueOf(var8), (Class)null, map, (String)"slashy string");
        byte var9 = 7;
        ScriptBytecodeAdapter.setProperty(Integer.valueOf(var9), (Class)null, map, (String)"dollar slashy string");
        Object closureQuote = "value";
        byte var11 = 8;
        ScriptBytecodeAdapter.setProperty(Integer.valueOf(var11), (Class)null, map, (String)ShortTypeHandling.castToString(new GStringImpl(new Object[]{closureQuote}, new String[]{"closure quote ", ""})));
        var1[2].callCurrent(this, map);
    }
    }
  5. 由上可知,Test.groovy编译成Test.class之后,会继承Script类,然后定义了两个构造方法、main方法,run方法,最后才是testQuotedIdentifiers方法。

  6. 在testQuotedIdentifiers方法中调用了ScriptBytecodeAdapter.createMap方法创建了map实例,代码如下:

    public static Map createMap(Object[] values) {
        return InvokerHelper.createMap(values);
    }
  7. 上述的方法只是一个封装,具体实现在InvokerHelper中:

    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;
    }
  8. 上述代码因为参数传递过来的是new Object[0],所以是直接new了一个LinkedHashMap实例就返回了。回到testQuotedIdentifiers中可以看到其实前面7种引用方式本质是一样,只有闭包引用方式有区别,但是最终都是ScriptBytecodeAdapter的setProperty方法完成map的添加操作。

字符串

  1. Groovy字符串分为好种:单引号字符串、双引号字符串、三引号字符串等,这些字符串均对应不同的应用场景。

  2. 单引号字符串:本质上是java的String,与java的String的使用方法也是一样。如下代码:

    void testSingleQuotedString() {
        def ss = 'SingleQuotedString'
        println(ss)
    }
    testSingleQuotedString()
  3. 上述定义了一个单引号字符串然后打印到控制台。再来看看编译过后的class代码:

    public void testSingleQuotedString() {
        CallSite[] var1 = $getCallSiteArray();
        Object ss = "SingleQuotedString";
        var1[3].callCurrent(this, ss);
    }
  4. 单引号字符串(包括双引号字符串)的限制是只能单行来使用,这与Groovy的语法特性有关。
  5. 三引号字符串:分为三单引号字符串和三双引号字符串,目的是为了解决单行限制,如下三单引号字符串代码:

    void testTripleSingleQuotedString() {
        def tss = '''line one 
    line two
    line three'''
    
        println(tss)
    }
    
    testTripleSingleQuotedString()
  6. 再看编译过后的class代码,原来Groovy中多行字符串的实现原理是加了\n换行符,其实现也是java的String对象:

    public void testTripleSingleQuotedString() {
        CallSite[] var1 = $getCallSiteArray();
        Object tss = "line one \nline two\nline three";
        var1[6].callCurrent(this, tss);
    }
  7. 双引号字符串:如果字符串中没有插值符,其底层实现是java的String,否则是groovy.lang.GString,如下实例:

    void testDoubleQuotedString() {
        def person = [:]
        person.'name' = 'Jack'
        def one = "person name:" + person.get("name")
        def two = "person name:$person.name "
        print(one + ' and ' + two)
    }
    
    testDoubleQuotedString()
  8. 上述代码定义了一个Map,在第一句println中是双引号字符串的普通使用,下一句是使用插值符用法。再来看看,编译后的class源码:

    public void testDoubleQuotedString() {
        CallSite[] var1 = $getCallSiteArray();
        Object person = ScriptBytecodeAdapter.createMap(new Object[0]);
        String var3 = "Jack";
        ScriptBytecodeAdapter.setProperty(var3, (Class)null, person, (String)"name");
    
        //双引号字符串的普通用法
        Object one = var1[2].call("person name:", var1[3].call(person, "name"));
    
        //双引号字符串的插值符用法
        Object two = new GStringImpl(new Object[]{var1[4].callGetProperty(person)}, new String[]{"person name:", " "});
    
        var1[5].callCurrent(this, var1[6].call(var1[7].call(one, " and "), two));
    }
  9. 由上可知双引号字符串普通用法实现是Java的String,而插值符用法则使用了GStringImpl。

  10. 双引号字符串的插值符合+闭包表达式,展现了Groovy语言强大的一面,如下代码:

    void testInterpolatingClosureExpressions() {
        def number = 1
        def eagerGString = "value == ${number}"
        def lazyGString = "value == ${-> number}"
    
        println(eagerGString)
        println(lazyGString)
    
        number = 2
    
        println(eagerGString)
        println(lazyGString)
    }
    
    testInterpolatingClosureExpressions()
  11. 上述的编译后的class如下:

    public void testInterpolatingClosureExpressions() {
        CallSite[] var1 = $getCallSiteArray();
        Reference number = new Reference(Integer.valueOf(1));
        Object eagerGString = new GStringImpl(new Object[]{number.get()}, new String[]{"value == ", ""});
        class _testInterpolatingClosureExpressions_closure1 extends Closure implements GeneratedClosure {
            public _testInterpolatingClosureExpressions_closure1(Object _thisObject, Reference number) {
                CallSite[] var4 = $getCallSiteArray();
                super(Test.this, _thisObject);
                this.number = number;
            }
    
            public Object doCall() {
                CallSite[] var1 = $getCallSiteArray();
                return this.number.get();
            }
    
            public Object getNumber() {
                CallSite[] var1 = $getCallSiteArray();
                return this.number.get();
            }
        }
    
        Object lazyGString = new GStringImpl(new Object[]{new _testInterpolatingClosureExpressions_closure1(this, number)}, new String[]{"value == ", ""});
        var1[2].callCurrent(this, eagerGString);
        var1[3].callCurrent(this, lazyGString);
        byte var5 = 2;
        ((Reference)number).set(Integer.valueOf(var5));
        var1[4].callCurrent(this, eagerGString);
        var1[5].callCurrent(this, lazyGString);
    }
  12. 区别是构造GStringImpl的参数不同,eagerGString使用的是Reference类型,而lazyGString使用的是_testInterpolatingClosureExpressions_closure1。

  13. Slashy字符串:即斜杠字符串,是为正则表达式设计的,原因是除了正斜杠需要转义,其它的均不再需要转义。它还支持多行、插值的特性,如下代码:

    void testSlashyString() {
        def fooPattern = /.*foo.*/
        println(fooPattern)
    
        def escapeSlash = /The character \/ is a forward slash/
        println(escapeSlash)
    
        def multilineSlashy = /one
        two
        three/
    
        println(multilineSlashy)
    
        def color = 'blue'
        def interpolatedSlashy = /a ${color} car/
    
        println(interpolatedSlashy)
    }
    
    testSlashyString()
  14. 再看编译过后的class代码,会发现与双引号字符串本质是一样的:

    public void testSlashyString() {
        CallSite[] var1 = $getCallSiteArray();
        Object fooPattern = ".*foo.*";
        var1[2].callCurrent(this, fooPattern);
        Object escapeSlash = "The character / is a forward slash";
        var1[3].callCurrent(this, escapeSlash);
        Object multilineSlashy = "one\n    two\n    three";
        var1[4].callCurrent(this, multilineSlashy);
        Object color = "blue";
        Object interpolatedSlashy = new GStringImpl(new Object[]{color}, new String[]{"a ", " car"});
        var1[5].callCurrent(this, interpolatedSlashy);
    }
  15. Groovy中字符类型要借助于字符串来辅助完成创建,方法如下:

    void testCharacters() {
        char c1 = 'A'
        assert c1 instanceof Character
    
        def c2 = 'B' as char
        assert c2 instanceof Character
    
        def c3 = (char) 'C'
        assert c3 instanceof Character
    }
    
    testCharacters()

数据类型

  1. 整型:Groovy中的byte、char、short、int、long和java.lang.BigInteger均跟Java一样,如下示例:

    void testIntegralLiterals() {
        // primitive types
        byte b = 1
        char c = 2
        short s = 3
        int i = 4
        long l = 5
    
        // infinite precision
        BigInteger bi = 6
    }
  2. 使用def来定义来整型数据,编译器会自动适配数据的类型,原则是在不丢失数据的前提下选取小容量类型,如下例子:

    void testAdaptIntegralType() {
        def a = 1
        assert a instanceof Integer
    
        // Integer.MAX_VALUE
        def b = 2147483647
        assert b instanceof Integer
    
        // Integer.MAX_VALUE + 1
        def c = 2147483648
        assert c instanceof Long
    
        // Long.MAX_VALUE
        def d = 9223372036854775807
        assert d instanceof Long
    
        // Long.MAX_VALUE + 1
        def e = 9223372036854775808
        assert e instanceof BigInteger
    }
  3. 除了十进制,Groovy同样支持二进制、八进制与十六进制,使用如下:

    void test() {
        //二进制,以0b开头
        int xInt = 0b10101111
        assert xInt == 175
    
        //八进制,以0开头  
        int xInt = 077
        assert xInt == 63
    
        //十六进制,以0x开头
        int xInt = 0x77
        assert xInt == 119
    }

Lists类型

  1. Lists:Groovy使用方括号来定义列表,如下示例:

    void testLists() {
        def numbers = [1, 2, 3]
        println('number:' + numbers.size())
    }
  2. 上述代码编译过的字节码如下:

    public void testLists() {
        CallSite[] var1 = $getCallSiteArray();
        Object numbers = ScriptBytecodeAdapter.createList(new Object[]{Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)});
        var1[2].callCurrent(this, var1[3].call("number:", var1[4].call(numbers)));
    }
  3. 通过ScriptBytecodeAdapter的createList创建了这个list,再看其源码:

    public static List createList(Object[] values) {
        return InvokerHelper.createList(values);
    }
    

    InvokerHelper中的createList方法

    public static List createList(Object[] values) {
        List answer = new ArrayList(values.length);
        answer.addAll(Arrays.asList(values));
        return answer;
    }
  4. 由上可知,Groovy的list类型就是java的list类型,而且默认使用的ArrayList类型。

  5. 一个list可存放多种类型数据,:

    void testDiffType() {
        def list = [1,'abc',true]
        println(list.size())
    }
  6. 上述的list默认都是ArrayList类型,可通过as标识符或明确指定类型来修改:

    void test() {
        def linkedList = [2, 3, 4] as LinkedList
        assert linkedList instanceof java.util.LinkedList
    
        LinkedList otherLinked = [3, 4, 5]
        assert otherLinked instanceof java.util.LinkedList
    }
  7. list元素:与一般的脚本语言类似,可使用正或负下标来访问元素,通过<<符合往列表尾部添加元素,具体如下:

    void testElementsOperation() {
        def letters = ['a', 'b', 'c', 'd']
    
        assert letters[0] == 'a'
        assert letters[1] == 'b'
    
        assert letters[-1] == 'd'
        assert letters[-2] == 'c'
    
        letters[2] = 'C'
        assert letters[2] == 'C'
    
        letters << 'e'
        assert letters[4] == 'e'
        assert letters[-1] == 'e'
    
        assert letters[1, 3] == ['b', 'd']
        assert letters[2..4] == ['C', 'd', 'e']
    }

Arrays类型

  1. Groovy的数组用法除了需要显式定义,其余用法与list一样,如下示例:

    void testArrays() {
        // 直接定义为数组
        String[] arrStr = ['one', 'second', '3']
        println('arrStr len:' + arrStr.length)
    
        // 使用as标识符指定为数组
        def numArr = [1, 2, 3] as int[]
        println('numArr len:' + numArr.length)
    }
  2. 上述代码的字节码如下:

    public void testArrays() {
        CallSite[] var1 = $getCallSiteArray();
    
        String[] arrStr = new String[]{"one", "second", "3"};
        var1[2].callCurrent(this, var1[3].call("arrStr len:", var1[4].callGetProperty(arrStr)));
    
        Object numArr = (int[])ScriptBytecodeAdapter.asType(ScriptBytecodeAdapter.createList(new Object[]{Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)}), int[].class);
        var1[5].callCurrent(this, var1[6].call("numArr len:", var1[7].callGetProperty(numArr)));
    }
  3. 上面as标识符代码:将ScriptBytecodeAdapter.createList创建出来的ArrayList与int[].class作为ScriptBytecodeAdapter.asType的参数,最终返回了int数组。

Map类型

  1. Groovy中Map的key是不限类型,基本用法如下:

    void testMap() {
        def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']
    
        colors['pink'] = '#FF00FF'
        colors.yellow = '#FFFF00'
    
        println('size:' + colors.size() + ",blue:" + colors.blue)
    }
  2. 当要获取Map的key时,需定义key这个变量,且定义map时添加圆括号,如下:

    void testMapKey() {
        def key = 'name'
        def person = [key: 'Guillaume']
        println("key:" + person.name)
    
        person = [(key): 'Guillaume']
        println("key:" + person.name)
    }
    
    结果:
    key:null
    key:Guillaume
  3. 上述key不加圆括号为null的原因是:person直接将key这字符串当作了map的key而不是将key的内容name作为key。

你可能感兴趣的:(Android,Groovy)