Groovy语法之操作符

算术运算
  1. Groovy的算术运算操作相比Java多了对幂运算的支持,用法如下:

    void testPowerOperators() {
        def power = 2 ** 5
        println(power)
    }
  2. 上述编译之后的字节码如下:

    public void testPowerOperators() {
        CallSite[] var1 = $getCallSiteArray();
        Object power = var1[2].call(Integer.valueOf(2), Integer.valueOf(5));
        var1[3].callCurrent(this, power);
    }
  3. 因无法从字节码文件上确定CallSite类型,无法给出上述幂运算实现细节的结论。

对象运算
  1. 相比Java,Groovy支持安全运算符(?.),可有效避免空指针异常,使用方法如下:

    void testSafeNavigationOperator() {
        Person person = null
    
        def name = person?.name
    
    //    def name1 = person.name
    
        println('person:' + person + ',name:' + name)
    }
  2. 安全运算符保证了上述代码正常运行,而注释起来的代码在运行时会直接抛异常,看编译过后的class代码来比较它们的区别:

    public void testSafeNavigationOperator() {
        CallSite[] var1 = $getCallSiteArray();
        Person person = null;
    
        Object name = var1[2].callGroovyObjectGetPropertySafe(person);
    
        Object name1 = var1[3].callGroovyObjectGetProperty(person);
    
        var1[4].callCurrent(this, var1[5].call(var1[6].call(var1[7].call("person:", person), ",name:"), name));
    }
  3. 从源码来看person?.name与person.name区别就在于前者调用callGroovyObjectGetPropertySafe方法,后者调用callGroovyObjectGetProperty方法。它们实现如下:

    public final Object callGroovyObjectGetPropertySafe(Object receiver) throws Throwable {
        if (receiver == null)
            return null;
        else
            return callGroovyObjectGetProperty(receiver);
    }
    public Object callGroovyObjectGetProperty(Object receiver) throws Throwable {
        if (receiver == null) {
            try {
                return InvokerHelper.getProperty(NullObject.getNullObject(), name);
            } catch (GroovyRuntimeException gre) {
                throw ScriptBytecodeAdapter.unwrap(gre);
            }
        } else {
            return acceptGroovyObjectGetProperty(receiver).getProperty(receiver);
        }
    }
  4. 上述两者区别就是callGroovyObjectGetPropertySafe比callGroovyObjectGetProperty多了判空操作。

  5. Groovy支持直接访问属性,相比java的get/set方法,代码会更加简洁,如下例子:

    class User {
        public final String name
    
        User(String name) { this.name = name }
    
        String getName() { "Name: $name" }
    }
    
    def user = new User('Bob')
    
    println(user.name)  // Name: Bob
    println(user.@name) // Bob
  6. 注意,上述代码user.name实际调用的是user.getName方法,而user.@name才是调用name这个属性。

  7. Groovy支持方法指针操作符(.&),原理是通过变量来存放某个方法的指针,即可使用这个变量来调用这个方法,如下示例:

    void testMethodPointerOperator() {
        def str = 'example of method reference'
        def fun = str.&toUpperCase
        println(fun()) // EXAMPLE OF METHOD REFERENCE
    }
  8. 上述代码的字节码如下:

    public void testMethodPointerOperator() {
        CallSite[] var1 = $getCallSiteArray();
        Object str = "example of method reference";
        Object fun = ScriptBytecodeAdapter.getMethodPointer(str, "toUpperCase");
        var1[2].callCurrent(this, var1[3].call(fun));
    }
  9. 继续追踪getMethodPointer方法:

    public static Closure getMethodPointer(Object object, String methodName) {
            return InvokerHelper.getMethodPointer(object, methodName);
        }
  10. 再看InvokerHelper的getMethodPointer,发现这个方法指针返回的其实是MethodClosure这个类:

    public static Closure getMethodPointer(Object object, String methodName) {
        if (object == null) {
            throw new NullPointerException("Cannot access method pointer for '" + methodName + "' on null object");
        }
        return new MethodClosure(object, methodName);
    }
  11. 上述MethodClosure涉及到了Groovy中的闭包概念,简单理解是将一个方法作为另一个方法的参数传递,具体用法如下:

    def transform(List elements, Closure action) {
        def result = []
        elements.each {
            result << action(it)
        }
        result
    }
    
    String describe(Person p) {
        "$p.name is $p.age"
    }
    
    // 定义了方法指针,本质是Closure的子类
    def action = this.&describe
    def list = [new Person(name: 'Bob', age: 42), new Person(name: 'Julia', age: 35)]
    
    //action作为参数传递
    assert transform(list, action) == ['Bob is 42', 'Julia is 35']
正则表达式
  1. 相比java,groovy提供模式运算符(~)可方便创建java.util.regex.Pattern的示例,如下代码:

    void testPatternOperator() {
        def p = ~/test/
        println(p)
    }
  2. 对应的字节码如下:

    public void testPatternOperator() {
        CallSite[] var1 = $getCallSiteArray();
        Object p = ScriptBytecodeAdapter.bitwiseNegate("test");
        var1[2].callCurrent(this, p);
    }
  3. 来看bitwiseNegate的实现:

    public static Object bitwiseNegate(Object value) throws Throwable {
        try {
            return InvokerHelper.bitwiseNegate(value);
        } catch (GroovyRuntimeException gre) {
            throw unwrap(gre);
        }
    }
  4. 实现是在InvokerHelper的bitwiseNegate方法中:

    public static Object bitwiseNegate(Object value) {
        if (value instanceof Integer) {
            Integer number = (Integer) value;
            return ~number;
        }
        if (value instanceof Long) {
            Long number = (Long) value;
            return ~number;
        }
        if (value instanceof BigInteger) {
            return ((BigInteger) value).not();
        }
        if (value instanceof String) {
            // value is a regular expression.
            return StringGroovyMethods.bitwiseNegate(value.toString());
        }
        if (value instanceof GString) {
            // value is a regular expression.
            return StringGroovyMethods.bitwiseNegate(value.toString());
        }
        if (value instanceof ArrayList) {
            // value is a list.
            List newlist = new ArrayList();
            Iterator it = ((ArrayList) value).iterator();
            for (; it.hasNext();) {
                newlist.add(bitwiseNegate(it.next()));
            }
            return newlist;
        }
        return invokeMethod(value, "bitwiseNegate", EMPTY_ARGS);
    }
  5. 语法基础篇可知这是一个String类型,所以最终会调用StringGroovyMethods.bitwiseNegate方法,具体实现如下:

    public static Pattern bitwiseNegate(String self) {
        return bitwiseNegate((CharSequence) self);
    }
    
    public static Pattern bitwiseNegate(CharSequence self) {
        return Pattern.compile(self.toString());
    }
  6. 由上可得出结论:groovy的模式运算符最终会被翻译成Pattern来实现。

  7. Groovy提供查找运算符(=~),对应的是java.util.regex.Matcher,最终是通过Pattern的matcher来实现,此处不再展开,例子如下:

    void testFindOperator() {
        def text = "some text to match"
        def m = text =~ /match/
    
        if (!m.find()) {
            throw new RuntimeException("Oops, text not found!")
        }
    
        println(m.getClass())//class java.util.regex.Matcher
    }
  8. 匹配运算符(==~)与查找运算符的区别:对应的匹配结果是Boolean类型,使用如下:

    void testMatchOperator() {
        def text = "some text to match"
        def m = text ==~ /match/
        if (m) {
            throw new RuntimeException("Should not reach that point!")
        }
        println(m.getClass())
    }
展开运算符
  1. 展开运算符(*.):继承Iterable接口的类都可使用展开运算符,作用是扩展列表的操作,底层是通过迭代来完成展开。如下例子:

    void testSpreadOperator() {
        def persons = [new Person('one', 1), new Person('ten', 10)]
        def nameList = persons*.name
        println('persons:' + persons + ',nameList:' + nameList)
        // persons:[name=one, age=1, name=ten, age=10],nameList:[one, ten]
    }
  2. 上述展开运算符可以方便的获取到person中的属性列表,看字节码实现:

    public void testSpreadOperator() {
        CallSite[] var1 = $getCallSiteArray();
        Object persons = ScriptBytecodeAdapter.createList(new Object[]{var1[2].callConstructor(Person.class, "one", Integer.valueOf(1)), var1[3].callConstructor(Person.class, "ten", Integer.valueOf(10))});
    
        Object nameList = ScriptBytecodeAdapter.getPropertySpreadSafe(Operators.class, persons, (String)"name");
    
        var1[4].callCurrent(this, var1[5].call(var1[6].call(var1[7].call("persons:", persons), ",nameList:"), nameList));
    }
  3. 再看ScriptBytecodeAdapter.getPropertySpreadSafe实现,最终也是借助于Iterator来实现:

    public static Object getPropertySpreadSafe(Class senderClass, Object receiver, String messageName) throws Throwable {
        if (receiver == null) return null;
    
        List answer = new ArrayList();
        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
            answer.add(getPropertySafe(senderClass, it.next(), messageName));
        }
        return answer;
    }
  4. 展开运算符不但能对列表中的属性进行展开,还能应用在方法参数、列表构造或者是map构造中,如下例子:

    void testSpreadOperator() {
        def rawAges = [12, 13]
        println(getTotalAge(*rawAges))
    
        def newAges = [10, 11, *rawAges]
        println(getTotalAge(*newAges))
    
        def m1 = [c:3, d:4]
        def map = [a:1, b:2, *:m1]
        println(map)
    }
    
    static int getTotalAge(int ... args) {
        int total = 0
        for (int i = 0; i < args.length; i++) {
            total += args[i]
        }
        return total
    }
区间运算符
  1. 区间运算符(..):用来表示两个操作数之间的范围集合,分为闭区间运算符与半闭区间运算符,使用区别如下:

    void testRangeOperator() {
        //闭区间运算符
        def range = 0..5
        println(range) // [0, 1, 2, 3, 4, 5]
        //半闭区间运算符
        def rangeExclusive = 0..<5
        println(rangeExclusive) // [0, 1, 2, 3, 4]
    }
  2. 上述编译之后的字节码如下:

    public void testRangeOperator() {
        CallSite[] var1 = $getCallSiteArray();
        Object range = ScriptBytecodeAdapter.createRange(Integer.valueOf(0), Integer.valueOf(5), (boolean)1);
        var1[2].callCurrent(this, range);
    
        Object rangeExclusive = ScriptBytecodeAdapter.createRange(Integer.valueOf(0), Integer.valueOf(5), (boolean)0);
        var1[3].callCurrent(this, rangeExclusive);
    }
  3. 由上可知,闭区间运算符与半闭区间运算符的区别在于ScriptBytecodeAdapter.createRange方法的最后一个参数值不同,如下代码:

    public static List createRange(Object from, Object to, boolean inclusive) throws Throwable {
        if (from instanceof Integer && to instanceof Integer) {
            int ifrom = (Integer) from;
            int ito = (Integer) to;
            if (inclusive || ifrom != ito) {
                return new IntRange(inclusive, ifrom, ito);
            } // else fall through for EmptyRange
        }
        if (!inclusive) {
            if (compareEqual(from, to)) {
                return new EmptyRange((Comparable) from);
            }
            if (compareGreaterThan(from, to)) {
                to = invokeMethod0(ScriptBytecodeAdapter.class, to, "next");
            } else {
                to = invokeMethod0(ScriptBytecodeAdapter.class, to, "previous");
            }
        }
    
        return new ObjectRange((Comparable) from, (Comparable) to);
    }
  4. 上述创建的是一个IntRange列表,它其实是AbstractList的子类,并实现了Range接口,而Range继承了List接口。

比较运算符
  1. 比较运算符:可以方便的比较变量的大小关系,如下例子:

    void testSpaceshipOperator() {
        println(123 <=> 123) // 0
        println('a' <=> 'e') // -1
    }
  2. 实现是compareTo,如下字节码:

    public void testSpaceshipOperator() {
        CallSite[] var1 = $getCallSiteArray();
        if (BytecodeInterface8.isOrigInt() && !__$stMC && !BytecodeInterface8.disabledStandardMetaClass()) {
            var1[3].callCurrent(this, ScriptBytecodeAdapter.compareTo(Integer.valueOf(123), Integer.valueOf(123)));
        } else {
            var1[2].callCurrent(this, ScriptBytecodeAdapter.compareTo(Integer.valueOf(123), Integer.valueOf(123)));
        }
    
        var1[4].callCurrent(this, ScriptBytecodeAdapter.compareTo("a", "e"));
    }
in操作符
  1. in操作符:与isCase 方法等效;而对于list,则与list.contains或 list.isCase等效,使用如下:

       void testInOperator() {
        def list = [1,2,3,4]
        println(1 in list)
        println(list.isCase(1))
        println(list.contains(1))
    }
  2. 从下面的字节码可以看出:isCase与contains实现是一样,而in操作符会被转成isCase方法,所以三者实现是一致的:

    public void testInOperator() {
        CallSite[] var1 = $getCallSiteArray();
        Object list = ScriptBytecodeAdapter.createList(new Object[]{Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3), Integer.valueOf(4)});
        var1[2].callCurrent(this, ScriptBytecodeAdapter.isCase(Integer.valueOf(1), list));
        var1[3].callCurrent(this, var1[4].call(list, Integer.valueOf(1)));
        var1[5].callCurrent(this, var1[6].call(list, Integer.valueOf(1)));
    }
as操作符
  1. as操作符:即强转操作符,相比java的强转,会更加安全,如下:

    void testAsOperator() {
        Integer x = 123
    
        String castStr = (String) x
        println(castStr)
    
        String asStr = x as String
        println(asStr)
    }
  2. 两种转换的区别如下:

    public void testAsOperator() {
        CallSite[] var1 = $getCallSiteArray();
        Integer x = Integer.valueOf(123);
    
        String castStr = (String)ShortTypeHandling.castToString(x);
        var1[2].callCurrent(this, castStr);
    
        String asStr = (String)ScriptBytecodeAdapter.asType(x, String.class);
        var1[3].callCurrent(this, asStr);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值