Groovy之Hook Method钩子方法
一般情况下当我们访问或修改不存在的属性、调用不存在的方法会抛出异常,而在Groovy中则可以通过实现相应的Hook Method钩子方法以避免异常发生
属性缺失
Groovy在MP元编程方面提供了丰富的特性,其中之一就是在访问、修改不存在的属性时,提供了一个钩子方法。下面即是一个简单的Groovy示例,当我们访问、修改Tiger类不存在的属性,即会抛出相应的异常
class MissPropertyDemo {
static void testTiger() {
Tiger tiger = new Tiger(name: "Tony", type: "TIGER")
assert tiger.name == "Tony"
assert tiger.type == "TIGER"
try {
// 访问不存在的属性
def age = tiger.age
} catch (Exception e) {
assert e instanceof MissingPropertyException
println("Happen Missing Property Exception #1")
}
try {
// 修改不存在的属性
tiger.age = 234
} catch (Exception e) {
assert e instanceof MissingPropertyException
println("Happen Missing Property Exception #2")
}
try {
// 访问不存在的静态属性
def remark = Tiger.remark
} catch (Exception e) {
assert e instanceof MissingPropertyException
println("Happen Missing Property Exception #3")
}
try {
// 修改不存在的静态属性
Tiger.remark = 234
} catch (Exception e) {
assert e instanceof MissingPropertyException
println("Happen Missing Property Exception #4")
}
}
}
class Tiger{
String name
String type
}
测试结果如下所示
而由于Groovy为属性缺失这一场景提供了相应了钩子方法:propertyMissing、$static_propertyMissing。以避免直接抛出相应的异常,示例代码如下所示
class MissPropertyDemo {
static void testLion() {
Lion lion = new Lion(name: "Tom", type: "LION")
assert lion.name == "Tom"
assert lion.type == "LION"
// 访问不存在的属性
def result = lion.age
assert result == "Not age Property For Get!"
// 修改不存在的属性
lion.age = 22
// 访问不存在的静态属性
result = Lion.remark
assert result == "Not remark Static Property For Get!"
// 修改不存在的静态属性
Lion.remark = "危险动物"
}
}
class Lion{
String name
String type
/**
* 实现访问不存在的属性的钩子函数
* @param propertyName
* @return
*/
def propertyMissing(String propertyName) {
return "Not ${propertyName} Property For Get!"
}
/**
* 实现修改不存在的属性的钩子函数
* @param propertyName
* @param args
*/
void propertyMissing(String propertyName, Object args) {
println "Not ${propertyName} Property! For Set, args: ${args}"
}
/**
* 实现访问不存在的静态属性的钩子函数
* @param propertyName
* @return
*/
static def $static_propertyMissing(String propertyName) {
return "Not ${propertyName} Static Property For Get!"
}
/**
* 实现修改不存在的静态属性的钩子函数
* @param propertyName
* @param args
*/
static void $static_propertyMissing(String propertyName, Object args) {
println "Not ${propertyName} Static Property For Set, args: ${args}"
}
}
测试结果如下所示,符合预期
方法缺失
当我们调用一个不存在的方式时,即会抛出异常
class MissMethodDemo {
/**
* 调用不存在的方法, 抛出异常
*/
static void testCat() {
Cat cat = new Cat(name:"Tom", type:"CAT")
try {
// 调用实例不存在的方法
cat.fly()
} catch (Exception e) {
assert e instanceof MissingMethodException
println("Happen Missing Method Exception #1")
}
try {
// 调用不存在的静态方法
Cat.run()
} catch (Exception e) {
assert e instanceof MissingMethodException
println("Happen Missing Method Exception #2")
}
}
}
class Cat {
String name
String type
}
测试结果如下,符合预期
对于非静态方法而言,我们可以实现methodMissing方法以避免抛出异常。示例代码如下所示
class MissMethodDemo {
/**
* 调用不存在的方法, 返回默认值
*/
static void testDog() {
Dog dog = new Dog(name:"Aaron", type:"DOG")
// 调用实例不存在的方法
String msg = dog.fly("5", "km")
assert msg == "[DOG] ==>> methodName: fly, args: [5, km]"
}
}
class Dog {
String name
String type
/**
* 实现方法缺失的钩子函数
* @param methodName
* @param args
* @return
*/
def methodMissing(String methodName, Object args) {
return "[DOG] ==>> methodName: ${methodName}, args: ${args}"
}
}
对于静态方法而言,我们可以实现$static_methodMissing方法以避免抛出异常。示例代码如下所示
class MissMethodDemo {
/**
* 调用不存在的静态方法
*/
static void testChicken() {
assert Chicken.getInfo() == "I'm a CHICKEN"
assert Chicken.calcPrice() == "Missing Static Method: calcPrice"
}
}
class Chicken {
static String type = "CHICKEN"
static String getInfo() {
return "I'm a ${type}"
}
/**
* 实现静态方法缺失的钩子函数$static_methodMissing, 以避免抛出MissingMethodException异常
* @param methodName
* @param args
* @return
*/
static def $static_methodMissing(String methodName, Object args) {
return "Missing Static Method: $methodName"
}
}
与此同时,我们还可以通过invokeMethod方法进行动态选择的方法调用,示例代码如下所示
class MissMethodDemo {
/**
* 调用不存在的方法, 动态处理
*/
static void testPig() {
Pig pig = new Pig(name:"Bob", type:"PIG")
// 调用实例不存在的方法
def result1 = pig.swim()
assert result1 == "Yes, Bob can Swim..."
// 调用实例不存在的方法
def result2 = pig.fly()
assert result2 == "Yes, Bob also can Fly~"
// 调用实例不存在的方法
def result3 = pig.eat()
assert result3 == null
}
}
class Pig {
String name
String type
String swimmable() {
return "Yes, ${name} can Swim..."
}
String flyable() {
return "Yes, ${name} also can Fly~"
}
/**
* 实现方法缺失的钩子函数
* @param methodName
* @param args
* @return
*/
def methodMissing(String methodName, Object args) {
// 新方法名
String newMethodName
switch (methodName) {
case "swim" : {
newMethodName = methodName + "mable"
break
}
case "fly" : {
newMethodName = methodName + "able"
break
}
default : newMethodName = null
}
if(newMethodName) {
def result = this.invokeMethod( newMethodName, null )
return result
}
return null
}
}
参考文献
Groovy In Action · 2nd Edition Dierk König、Guillaume Laforge著
评论