算术运算
-
Groovy的算术运算操作相比Java多了对幂运算的支持,用法如下:
void testPowerOperators() {
def power = 2 ** 5
println(power)
} -
上述编译之后的字节码如下:
public void testPowerOperators() {
CallSite[] var1 = $getCallSiteArray();
Object power = var1[2].call(Integer.valueOf(2), Integer.valueOf(5));
var1[3].callCurrent(this, power);
} 因无法从字节码文件上确定CallSite类型,无法给出上述幂运算实现细节的结论。
对象运算
-
相比Java,Groovy支持安全运算符(?.),可有效避免空指针异常,使用方法如下:
void testSafeNavigationOperator() {
Person person = null
def name = person?.name
// def name1 = person.name
println('person:' + person + ',name:' + name)
} -
安全运算符保证了上述代码正常运行,而注释起来的代码在运行时会直接抛异常,看编译过后的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));
} -
从源码来看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);
}
} 上述两者区别就是callGroovyObjectGetPropertySafe比callGroovyObjectGetProperty多了判空操作。
-
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 注意,上述代码user.name实际调用的是user.getName方法,而user.@name才是调用name这个属性。
-
Groovy支持方法指针操作符(.&),原理是通过变量来存放某个方法的指针,即可使用这个变量来调用这个方法,如下示例:
void testMethodPointerOperator() {
def str = 'example of method reference'
def fun = str.&toUpperCase
println(fun()) // EXAMPLE OF METHOD REFERENCE
} -
上述代码的字节码如下:
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));
} -
继续追踪getMethodPointer方法:
public static Closure getMethodPointer(Object object, String methodName) {
return InvokerHelper.getMethodPointer(object, methodName);
} -
再看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);
} -
上述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']
正则表达式
-
相比java,groovy提供模式运算符(~)可方便创建java.util.regex.Pattern的示例,如下代码:
void testPatternOperator() {
def p = ~/test/
println(p)
} -
对应的字节码如下:
public void testPatternOperator() {
CallSite[] var1 = $getCallSiteArray();
Object p = ScriptBytecodeAdapter.bitwiseNegate("test");
var1[2].callCurrent(this, p);
} -
来看bitwiseNegate的实现:
public static Object bitwiseNegate(Object value) throws Throwable {
try {
return InvokerHelper.bitwiseNegate(value);
} catch (GroovyRuntimeException gre) {
throw unwrap(gre);
}
} -
实现是在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);
} -
从语法基础篇可知这是一个String类型,所以最终会调用StringGroovyMethods.bitwiseNegate方法,具体实现如下:
public static Pattern bitwiseNegate(String self) {
return bitwiseNegate((CharSequence) self);
}
public static Pattern bitwiseNegate(CharSequence self) {
return Pattern.compile(self.toString());
} 由上可得出结论:groovy的模式运算符最终会被翻译成Pattern来实现。
-
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
} -
匹配运算符(==~)与查找运算符的区别:对应的匹配结果是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())
}
展开运算符
-
展开运算符(*.):继承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]
} -
上述展开运算符可以方便的获取到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));
} -
再看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;
} -
展开运算符不但能对列表中的属性进行展开,还能应用在方法参数、列表构造或者是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
}
区间运算符
-
区间运算符(..):用来表示两个操作数之间的范围集合,分为闭区间运算符与半闭区间运算符,使用区别如下:
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]
} -
上述编译之后的字节码如下:
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);
} -
由上可知,闭区间运算符与半闭区间运算符的区别在于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);
} 上述创建的是一个IntRange列表,它其实是AbstractList的子类,并实现了Range接口,而Range继承了List接口。
比较运算符
-
比较运算符:可以方便的比较变量的大小关系,如下例子:
void testSpaceshipOperator() {
println(123 <=> 123) // 0
println('a' <=> 'e') // -1
} -
实现是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操作符
-
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))
} -
从下面的字节码可以看出: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操作符
-
as操作符:即强转操作符,相比java的强转,会更加安全,如下:
void testAsOperator() {
Integer x = 123
String castStr = (String) x
println(castStr)
String asStr = x as String
println(asStr)
} -
两种转换的区别如下:
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);
}